RubiksGrid

A field of Rubik's cubes in isometric projection, each turning a layer at a time and solving itself.

each cube scrambles then undoes itself, which is why it loops

Paste into Claude Code, Cursor or any agent. Pinned to a tag, so it will not change under you.

Options

density 6

2 to 16, good: 4 to 10

How much of the field you see. It opens the camera rather than resizing the cubes, so a higher number is more cubes at the same size rather than the same cubes smaller. The sketch calls this zoom. 6 is its base and is what the demo shows.

scheme classic

classic · neon · ocean · sunset · forest · pastel · monochrome

The seven palettes from the original sketch, carried over exactly. Each one carries its own body colour as well as its six face colours, because the plastic between the stickers is part of the palette: pastel on black is a different idea from pastel on its own near-black.

turn 0.4

0.08 to 2 s, good: 0.25 to 0.6

Seconds one quarter turn takes. The easing inside it is a finger flick rather than a symmetric curve: quick away, slow home, with the weight at the end. That is the single thing that makes the motion feel like fingers instead of like a motor.

rest 0.2

0 to 3 s, good: 0.1 to 0.6

Seconds a cube sits still between turns. Most of the charm is here. At 0 nothing ever stops and the field reads as a machine; a pause lets each turn land before the next one starts.

moves 5

1 to 20, good: 3 to 8

Turns of scramble before the cube undoes them and returns to solved. The whole loop is twice this, so it is also what sets the length of the animation: at the default beat, 5 moves is a six second cycle.

sync false

true · false

Every cube turns at the same moment rather than each on its own offset. Worth trying both: in sync the field reads as one object breathing, and out of sync it reads as a room full of people each solving their own.

tilt 30

0 to 60 deg, good: 24 to 38

How far the field tilts out of flat. 30 with the fixed eighth-turn is the classic isometric pair, and it is why a cube drawn this way shows three faces of equal width. Away from 30 it is still a nice axonometric view, just not an isometric one.

seed 7

1 to 9999

Seed for the scramble. The same seed gives the same sequence in the same order, which is what makes a recording reproducible.

maxDpr 2

1 to 3

Device pixel ratio ceiling. Flat isometric faces meeting at hard edges alias badly, so this is worth leaving at 2.

reducedMotionTime 1.2

0 to 30 s

The single frame shown when the user prefers reduced motion. Pick a moment mid-scramble rather than 0: at 0 every cube is solved and the field is six flat colours, which says nothing about what the item does.

What it needs

Category
Backdrops, an effect
Dependencies
three@>=0.160.0
Browser
WEBGL2
Frame budget
~2.5 ms · 1 context
Pinned to
v0.13.0
  • Adapted from a standalone sketch of mine, Isometric Rubik's, which is MIT. The idea came from a post on r/creativecoding. The look is matched deliberately; the mechanism underneath it is not the same, for one reason
  • The sketch keeps its state. Pick a move, reparent the nine cubies of that layer to a pivot, turn the pivot, reparent them back with positions snapped, push the move onto a history. Every frame is built from the one before it
  • renderAtTime has to answer for any moment without having seen the moments before it, so the recorder can seek and so a loop closes exactly. So nothing is mutated and no history is kept: every move is derived from the seed and the cube's place, and the state is replayed from solved each frame
  • The cubie state is integer: a position in {-1,0,1} and a 3x3 rotation of -1, 0 and 1. Replaying composes hundreds of turns and floating point drifts over that, which on a flat isometric face shows as a seam on a cube that is supposed to be solved. The sketch solves the same problem by snapping positions after every turn
  • The loop closes because the sequence undoes itself. Each cube scrambles for `moves` turns then replays them backwards, which is the inverse scramble, and is the sketch's solve button turned into the shape of the animation rather than a control on it
  • Cube offsets are whole turns, never fractions, or the field never returns to the frame it started on and a recording jumps once per loop
  • The cubes are laid in a staircase, not a grid. Each row is pushed sideways by half a cube so the V between two cubes in a row is filled by the one below, and the rows sit closer together than a cube is tall. The first version here used a rectangular grid with a gap and it read as a shelf of ornaments rather than as a pattern
  • The field covers the frame rather than fitting inside it. A cube clipped by the edge is correct; a tidy margin of background around a rectangle of cubes is what makes it look like a widget
  • No lights at all, and that is the look. The sketch uses MeshBasicMaterial throughout, so a face of a given colour is that exact colour wherever it is and whichever way it points. Lambert with an ambient and a key was tried first and it pulled the reds to a dusty rose and the whites to grey
  • three's colour management is left on, which is its default and what the sketch relies on. Turning it off skips the sRGB-to-linear conversion on the way in but not the linear-to-sRGB on the way out, so every colour is brightened exactly once and classic red comes out pink
  • A cubie fills 0.88 of its cell, which is the sketch's ratio. The sticker covers the whole cubie face rather than sitting inset on it, so the only black is the gap between cubies; an inset tile gives every cubie a second outline and doubles the black in the picture
  • Orthographic camera, because isometric is a projection with no perspective in it: a cube at the edge has to be the same size and shape as one in the middle, or the field stops tiling and starts being a scene
  • Seven draw calls whatever the grid size. The sketch gives every cubie its own mesh with six materials; here the bodies are one InstancedMesh and the stickers are one per colour
  • Which faces of a cubie carry a sticker is decided by where it started, not where it is now. That is what keeps nine of each colour however scrambled it gets
  • The middle layer is never turned. It moves no corner, so at this size it reads as a glitch rather than as a move
  • Seven draw calls regardless of grid size. The real per-frame cost is writing the instance matrices: at the default density that is roughly 1,100 cubie bodies and 2,200 stickers
  • Every working matrix and vector is allocated once. The inner loop runs tens of thousands of times a second and a matrix made in there is a matrix the collector has to take away again
  • The move sequences are cached per cube. They are pure functions of the seed and the index so they could be recomputed every frame, but it would be the same answer
  • Cost goes with the square of `density`, since it opens the camera in both directions. 6 is comfortable, 12 is four times the work

Concept credit: A grid of Rubik's cubes in isometric projection with layers turning, from a procedurally generated Rubik's cube pattern posted to r/creativecoding. The standalone sketch this is adapted from is my own and is MIT; the idea was theirs, by u/Positive_Tea_1166 on r/creativecoding. The implementation here is written from scratch.