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.
Like
Download
Next
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.
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.
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.
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.
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.
Look again
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
Back Next
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.
Start
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.
Sunny Cloudy Rainy Night
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.
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 Menu
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.
Assemble Scatter Bolt Heart Sword Planet
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.
Light 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.
Below Above Left Right Center
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.
Bubble Dots
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" });