Experiment 11
Procedural Animations
The loading animation, taken apart and driven by code, a clock, the viewport and the cursor
Published: Aug 16, 2026
Experiment 11
The loading animation, taken apart and driven by code, a clock, the viewport and the cursor
Published: Aug 16, 2026
Open to new projects, ideas, and conversations
Experiment 11
The loading animation, taken apart and driven by code, a clock, the viewport and the cursor
Published: Aug 16, 2026
The top-left tile plays the Blender export as it is: five clips through an AnimationMixer. The tile beside it drops the clips and rebuilds the motion as a GSAP timeline on four scalars — a smoothstep extension and a power4 snap-back for the corners, a decaying sine for the wobble whose first swing carries on the overshoot's momentum, and the centre's tumble as two simultaneous turns on two square world axes: a full 360° about the model's diagonal, which is the net turn the clip makes, and a full 360° about the axis square to it, which throws the spin axis ~90° off-centre mid-flight and is what makes it read as a tumble rather than a pinwheel. Both land on whole turns, so the pose comes home exactly. The clip's own rotation axis swings around mid-flight and no pair of fixed axes reproduces that, but on a 12-fold symmetric object the beats are what the eye reads, so the timing is fitted instead: a p⁵ wind-up that holds the roll back while the corners travel, a whip that peaks near 1950°/s, and a settle that falls back through a 20° overshoot fast enough to hand the wobble its momentum. Swept angle comes out at 644° against the clip's 637°, and per-frame speed within 2.4°. Neither tile advances itself. Both are seeked from one clock derived from the frame timestamp, so they cannot drift, and because the extension and wobble targets run 0..1 the sliders scale the motion live without rebuilding it.
The cube uses an orthographic camera, which turns the fullscreen fit into arithmetic: a face's on-screen side is size × scale × pixels-per-unit, so the target scale is max(viewport width, height) divided by that — the face covers the screen, cropping the shorter dimension. On play the tile's pixels-per-unit is frozen, the wrapper goes position: fixed(the frustum grows in world units, so nothing jumps), and the cube is offset to where its tile was and eased to the centre while it grows in log-space and slerps to whichever of the cube's 24 face-on rotations lies ahead along its spin, so the tumble never reverses. Before it returns, the tile is measured again so it lands wherever the page has scrolled to.
The last tile reads the pointer anywhere on the page and turns it into a look-at from a virtual depth in front of the tile, clamped to a maximum tilt and exponentially smoothed; without a pointer a two-frequency sine drift takes over and the two blend. Each corner block's diagonal is projected to the screen through the current rotation and compared with the cursor direction, and all four extend by a soft weight of that alignment, scaled by how far off-centre the cursor is — so the hand-off between corners is continuous rather than a switch, and hovering the model itself extends nothing.