Mobile game · Designed & built end-to-end

A cozy voxel-sculpture
merge puzzle.

Drop coloured cubes to fill ghosted 3D sculptures, chain perfect clears across a five-level roguelike run, and grow a gallery of everything you finish. One thumb, no timer, no stress. I designed and built the whole thing solo — game design, art direction, and code.

Playable · live

Play Cubelets

The full game, live in your browser. 16 hand-built levels, a daily challenge, a gallery, and three palette skins. Drag to aim, release to drop.

Open the game
Reference

Design system

Monument-Valley pastel tokens, the seven cube hues, warm glass surfaces, and the voxel-heart mark — the full, code-truthful spec.

View system
Engineering

Under the hood

React 19 · PixiJS 8 · Three.js · Capacitor 8. Procedural Web-Audio score, an offline PWA, and a native Android build — all at a steady 60fps.

See the build
The problem

Casual puzzlers are either mindless tappers or punishingly tight. I wanted the cozy middle.

The "merge" genre is enormous, but most of it runs on stress — timers, pop-ups, energy meters, dark-pattern monetisation. I wanted a merge-and-build loop that rewards spatial thinking instead of reflexes: drop a cube, watch a 3D sculpture slowly emerge, and feel good when it clicks into place. Satisfying without being demanding — something you reach for to unwind, not to grind.

The flow

One loop, five beats. Easy to learn, hard to put down.

I sketched the core loop on paper first — deliberately low-fi, so the structure had to earn its keep before a single cube got rendered.

a single cozy loop from "here's your shape" to "saved to the gallery"
1
your shape
a ghosted 3D
sculpture to fill
2
drop a cube
drag to aim,
release to drop
3
smart-snap
right tile locks in,
wrong one stacks
as junk
4
clear junk
3 same-colour
junk tiles merge
& vanish
5
sculpture
complete
perfect clear?
big bonus. saved
to your gallery.
the snap is forgiving on purpose
no timer. ever.
Three design bets

The decisions that shaped everything else.

"Cozy, not stressful" is easy to say and hard to build. Three early bets turned it into a system — how forgiving placement should be, where the interface sits, and what actually keeps you coming back.

01

Smart-snap, not pixel-perfect.

Drop a correct-colour tile anywhere near its slot and it auto-locks into the sculpture. The game is about building the picture, not threading a needle — so placement forgives, and the satisfaction comes from the shape filling in, not from twitch precision. Mis-drops aren't punished; they just stack as clearable junk.

Exact (col, row)  →  Generous snap radius
02

The scene is the screen.

The 3D voxel field is the whole interface. Every menu, HUD, and panel floats above it as a single layer of warm translucent glass — you can always see your sculpture behind the chrome. No solid full-bleed panels ever come between the player and the thing they're making.

Opaque UI screens  →  One layer of glass over the field
03

Reward the run, not the score.

A five-level roguelike run with modifier cards between levels (double score, slow time, hidden preview), a perfect-clear bonus, a gallery that keeps every sculpture you finish, and a deterministic daily challenge with a "beat yesterday's ghost" pip. The pull is progress and collection — no energy meters, no nags.

Chase a high score  →  Collect, complete, come back
UX choices that matter

Small calls that change how the whole thing feels.

Most of what makes a game feel good rather than just functional is a pile of tiny, invisible decisions. Here are the ones I'd defend in a critique.

a.

A generous snap radius you never see.

The correct tile locks in from a wider area than feels "fair" — tuned by hand until dropping felt confident instead of fiddly. Players read it as "I'm good at this," not "the game is helping me." Forgiveness you don't notice is the best kind.

b.

Mistakes become a second puzzle, not a penalty.

A wrong drop doesn't cost you — it stacks as a coloured junk tile. Line up three of a colour and they merge and vaporise. So the "fail" state is just another satisfying thing to clear, which keeps a bad run from spiralling.

c.

The cube colours are the only loud thing.

Chrome stays warm and desaturated; saturated hue is reserved entirely for the cubes. So any colour in the UI reads instantly as a gameplay hint, and the sculpture is always the brightest thing on screen.

d.

A daily "ghost" instead of a leaderboard.

The daily challenge shows a pip of yesterday's best score that flips to "BEAT GHOST" when you overtake it. You compete with your past self — all the pull of a leaderboard, none of the backend, and zero social pressure.

e.

Feedback you feel, on a ladder.

Combos climb a ten-step pitched snap-sound ladder and a tiered haptic pattern, so a hot streak literally rises in your hand and your ears. The final cube of a level gets its own micro punch — a scale pump before the celebration fires.

f.

Reduced-motion is a real mode, not a toggle.

With prefers-reduced-motion on, every bounce, spin, and confetti burst collapses to a plain opacity fade. The game stays fully playable and still feels intentional — accessibility designed in, not bolted on.

Behind the design

One warm system, used everywhere.

Cubelets runs on a "Monument Valley pastel" system — warm cream, espresso text, and seven soft cube hues that double as the game's palette and its brand. Tokens are defined once and re-bound per palette skin. Here's the short version; the full spec lives on the Design page.

Color · 7 cube hues · 1 family

The cube colours are the brand.

No pure primaries — every hue has a soft watercolour quality. These seven are simultaneously the brand swatch and the exact colours dropped onto the field, so the moment one appears in the chrome, your eye reads it as a hint. They ride on a warm cream background (#F5E4CB) with espresso text, never cold black.

Violet
#8B5A7D
The brand accent, primary action
Magenta
#D87888
Hot — danger & the R tile
Coral
#E8927D
Warm-pink, gradient start
Gold
#E6B94A
Bright — focus ring & awards
Cyan
#5FB3A8
The B tile, alternate glow
Green
#7FBFA0
The G tile, success
Type · rounded, to match the cubes

Rounded letterforms, two weights of warmth.

Fredoka Display · the score · the title
Sculpture complete!

Chunky, friendly, unmistakably a game. Its rounded corners echo the rounded cubes. Reserved for the big moments.

Nunito Body · UI · everything else
Drag to aim, release to drop. Line up three junk tiles to clear them. No timer — take your time.

One family at weight 600, set on the root so everything inherits it. Soft, legible, and quietly rounded to match.

JetBrains Mono Eyebrows · counts · spec labels
×4 COMBO  ·  16 LEVELS  ·  25 ◆

Only where a label needs to read as "system" — section eyebrows, prism counts, the design-spec voice you're reading now.

Shape · 4 radii · all soft

Soft on glass, soft on chips.

Nothing sharp anywhere — the whole system is rounded to sit next to the rounded cubes. Four steps, from the score-HUD pill down to the smallest badge.

Pill Score HUD, play CTA, modifier picker
999px
Card Glass panels, gallery tiles
18px
Button In-game actions
12px
Chip Small badges, mini buttons
8px
Identity · the mark

The icon is the whole game in one frame.

Cubelets app icon — a voxel heart built from cubes
Cubelets
A voxel heart, mid-merge

The app icon is a heart built from dark and red cubes, with a single pink cubelet sitting just off-centre — the merge gameplay reduced to one still frame. It reads warm and friendly at thumbnail size and tells you what the game is before you've read a word. In-product glyphs are inline line icons drawn at the same 1.6px stroke as the body text, so the chrome and the type feel cut from one tool.

Motion · 4 patterns

Motion as feedback, not decoration.

Cube drop 280ms ease-out, small bounce on landing
+250
Score tick number scales 1 → 1.15 → 1
menu
Panel open glass scales 0.96 → 1, fades in
junk
Mis-drop gentle shake — a soft, no-penalty "no"
See the full design spec Play Cubelets
What's deliberately missing

The cuts I'd defend.

A solo game has to be ruthless about scope. Here's what I left out on purpose — each cut keeps the game cozy, offline, and free of the dark patterns the genre is famous for.

No backend

No global leaderboard, no servers, no analytics SDK. A worldwide leaderboard needs infrastructure I didn't want to run for a solo game — and it invites the exact stress I was avoiding. The deterministic daily "ghost" gives you something to chase that's entirely local.

No accounts

Progress lives in local storage, not a login. Your gallery, prisms, and streaks are on-device. Zero sign-in friction, nothing to harvest, and you can start playing in the time it takes the first cube to fall. The trade-off is no cross-device sync — a fair price for a casual title.

No IAP

Palette skins are earned with in-game prisms, not bought. The shop is a stub on purpose — no real-money purchases, no energy timers, no "watch an ad to continue." The whole point was a merge game you can trust to leave you alone.

Scoped levels

16 hand-built sculptures + roguelike runs, not hundreds of hand-crafted puzzles. Bespoke one-solution levels and mid-run bonus rooms are designed but deferred — the modifier-driven runs already make each playthrough feel different without that authoring cost.

Under the hood

Built solo, on a modern web stack.

One codebase ships to the browser, to an installable PWA, and to native Android & iOS — at a steady 60fps. No game engine licence, no asset pipeline; even the music is synthesised at runtime.

Frontend

React 19 · Vite 8

  • Component UI, hooks-based state, no heavyweight store
  • Local-first persistence in localStorage
  • Offline-capable service worker + installable manifest
  • Safe-area aware, responsive, one-thumb layout
Game

PixiJS 8 · Three.js

  • Three.js voxel field + orbiting gallery viewer
  • PixiJS for HUD, particles, and celebration scenes
  • Procedural music & SFX via Web Audio — zero audio files
  • GPU-accelerated, 60fps, prefers-reduced-motion aware
Native

Capacitor 8

  • Same web build wrapped for Android & iOS
  • Native haptics on combos and clears
  • Target SDK 35, signed bundle for Google Play
  • "Add to Home Screen" installable on the web too