Recipes

Twenty-eight ways to put cube icons on a page, each with the code that makes it. Every one needs only icons.js and cubes.js (see installation), plus the lines under it.

Button icons

Navigation

Small icons that move with intent: the heart pops and springs back; the arrows nudge while you hover.

const heart = CubeIcons.stage(canvas, { look: "soft", tilt: false, duration: 900, ease: "elastic" });
heart.show(HEART);
btn.addEventListener("pointerenter", () => heart.explode(null, null, 1.6));

// a nudge: flip between two frames while hovered
let t; const frames = [ARROW_UP, ARROW_DOWN];
btn.addEventListener("pointerenter", () => { let i = 0; t = setInterval(() => arrow.show(frames[++i % 2]), 420); });
btn.addEventListener("pointerleave", () => { clearInterval(t); arrow.show(frames[0]); });

Tabs

Navigation

One stage morphs between tabs: the cubes fly from one icon straight into the next.

tab.addEventListener("click", () => stage.show(tab.dataset.icon));

Feature cards

Navigation

One stage per card, isometric and soft, built while you hover or focus the card.

Fast

Ships in a day, not a sprint.

Private

Your files stay yours.

Everywhere

Works in any browser.

const s = CubeIcons.stage(canvas, { view: "iso", look: "soft", depth: 1, tilt: false });
card.addEventListener("pointerenter", () => s.show("rocket"));
card.addEventListener("pointerleave", () => s.clear());

Loading

Feedback

Progress drives the build, bottom row first; at 100% it turns into a tick.

0%
stage.build("rocket", progress);   // 0–1, as progress comes in
if (progress === 1) stage.show("check");

Form result

Feedback

A tick when it worked, a cross when it didn't. Try an address without an @.

 

form.addEventListener("submit", (e) => {
  e.preventDefault();
  stage.show(input.value.includes("@") ? "check" : "close");
});

Notification bell

Feedback

New message: the bell's cubes jolt and spring back, and the count goes up.

0
const bell = CubeIcons.stage(canvas, { duration: 800, ease: "elastic", tilt: false });
bell.show("bell");
function notify() { bell.explode(null, null, 1.3); badge.textContent = ++count; }

Star rating

Feedback

Five tiny stages. Unlit stars are tinted grey; as you hover, their cubes blend to gold.

Rate your visit

stars.forEach((s, i) => s.set({ tint: i < rating ? null : "#9a948a" }));

Drop zone

Feedback

Drag a file over it (or click): a folder builds to catch it, then a tick.

Drop a file here

zone.addEventListener("dragenter", () => stage.show("folder"));
zone.addEventListener("dragleave", () => stage.clear());
zone.addEventListener("drop", () => stage.show("check"));

Empty state

Feedback

Builds once it's half in view. The button replays it.

Nothing here yet

Your saved items will show up here.

CubeIcons.scroll(stage, "ghost", { mode: "trigger" });

Onboarding steps

Feedback

Each step morphs into the next, flying in from the side you're heading.

Make your profile
stage.show(steps[i].icon);   // the same cubes re-form for each step

Hero

Content

A headline with an object that keeps changing. Soft look on a warm page.

New · Version 2

Small ideas, built to be noticed.

Cubes, a little light, and a lot of motion.

Get started
CubeIcons.sequence(CubeIcons.stage(canvas, { look: "soft", gap: 0.05 }), ["rocket", "bulb", "trophy"], { every: 2600 });

Text in cubes

Content

A built-in 5-row pixel font: A–Z, 0–9 and a little punctuation.

stage.show(CubeIcons.text("Hello", { color: "B" }));

Live clock

Content

The time in cubes. Each second the colon blinks and changed digits re-form.

setInterval(() => {
  const d = new Date(), pad = (n) => String(n).padStart(2, "0");
  stage.show(CubeIcons.text(pad(d.getHours()) + (d.getSeconds() % 2 ? " " : ":") + pad(d.getMinutes()), { color: "C" }));
}, 1000);

Countdown

Content

3, 2, 1, launch. Each number flies in from below.

for (const n of ["3", "2", "1"]) { stage.show(CubeIcons.text(n, { color: "O" })); await wait(900); }
stage.show("rocket");

Weather

Content

Pick the weather: the same cubes re-form and their colours blend.

stage.show(forecast.icon);   // sun · cloud · umbrella · moon

Initials avatar

Content

Initials in cubes on a colour picked from the name.

const initials = name.split(" ").map((w) => w[0]).join("").slice(0, 2);
stage.show(CubeIcons.text(initials, { color: "W" }));

Reading progress

Content

Scroll this page: the trophy builds as you get closer to the bottom.

0%

of this page read

addEventListener("scroll", () => {
  const p = scrollY / (document.documentElement.scrollHeight - innerHeight);
  stage.build("trophy", p);
});

Menu reveal

Interaction

Open the menu: the hero object bursts toward you, the page blurs, and the cubes gather beside the links. Hover a link and they take its shape. Glow and float keep it alive.

STUDIO

Let's make
something bright

const look = { look: "soft", tint: "#3cff3c", glow: 0.8, float: 0.6, gap: 0.04 };
const hero = CubeIcons.stage(heroCanvas, { ...look, x: 0.72 });
const menu = CubeIcons.stage(menuCanvas, look);
hero.show("rocket");

button.addEventListener("click", () => {
  hero.burst();                                   // the object rushes at you and fades
  page.classList.add("open");                     // blur the page, fade the menu in (CSS)
  menu.scatter(90, { icon: "rocket" });           // loose cubes...
  setTimeout(() => menu.show("rocket"), 350);     // ...gather into the object
});
link.addEventListener("pointerenter", () => menu.show(link.dataset.icon));

Floating cubes

Interaction

Loose, glowing cubes drift across a section. Assemble gathers them into an icon, which keeps floating; Scatter lets them go again.

const stage = CubeIcons.stage(canvas, { look: "soft", tint: "#3cff3c", glow: 0.8, float: 0.7 });
stage.scatter(140, { icon: "bolt" });          // loose cubes drift across the canvas
button.onclick = () => stage.show("bolt");      // they gather into the icon, and keep floating

Morph sequence

Interaction

The same cubes fly from one icon to the next on a timer.

CubeIcons.sequence(stage, ["home", "bag", "cart", "gift", "trophy"], { every: 1800 });

Click to explode

Interaction

Click anywhere on the star. Cubes near the click fly furthest, then settle back.

canvas.addEventListener("click", (e) => stage.explode(e.clientX, e.clientY));

Cursor magnet

Interaction

Move over the face: the cubes near the pointer pop toward you.

CubeIcons.stage(canvas, { magnet: 1.6, tilt: false }).show("smile");

Theme toggle

Interaction

The switch's sun turns into a moon as the card goes dark.

Your choice is saved on this device.

toggle.addEventListener("click", () => { dark = !dark; stage.show(dark ? "moon" : "sun"); });

Fly-in direction

Interaction

Where hidden cubes start from. Each cube lines up with the spot it's flying to.

CubeIcons.stage(canvas, { from: "above" });   // below · above · left · right · center

Every look

Style

Eleven looks, one option. Light looks sit on cream, dark ones on black. Click any to replay it.

CubeIcons.stage(canvas, { look: "candy" });   // flat · soft · clay · candy · paper · outline · pixel · glass · neon · wire · holo

Your colours

Style

Swap any palette key for your own colour, in hex or rgb.

stage.set({ palette: { ...CubeIcons.PALETTE, B: "#ff4d8d" } });

Depth and view

Style

How many cubes deep each pixel goes, and which way you look at it. Move the mouse to tilt.

stage.set({ depth: 4, view: "iso" });