Nine passes, in order
The ring is drawn once into a half-float buffer, then handed down a fixed sequence. Order matters here — the plates have to be pushed back above white before anything tries to bloom them, dispersion has to happen while those highlights are still hot, and the gamut move has to land after the tone map or it grades the wrong numbers. Values update live as you move the rack.
-
01
– × –
Raycast to HDR
Seven plates on a ring, intersected analytically against the camera ray — no mesh, no depth buffer. The floor traces a second bounce for the reflection.
-
02
0.85
Plate sampling and motion
Seven 1024 × 720 photographs in one sRGB texture array, so a sample arrives already linearised. The mip level is computed from the ray footprint rather than from screen derivatives — a raycaster cannot hand the GPU trustworthy derivatives across a silhouette or through a mirror bounce, and letting it guess smears the edges and makes the reflection crawl.
Each plate is then read through its own current, solved every frame on a ping-pong pair of half-float buffers. Only what cannot be filmed convincingly is given a current. Ink, the oil film and the magnetised fluid are carried by their currents; glass, crystal, anodised metal and foil hold their geometry and move by highlight and hue, which is what they physically do when they turn. The lookup is inset first so a warped sample never reaches the clamped edge and drags it. Two plates also shift hue with incidence angle, which is what an interference coating really does — watch the dichroic glass re-colour as the ring carries it past you.
-
03
×6.5
Highlight expansion
The plates are eight-bit JPEG, so nothing in them exceeds white and a bloom would have nothing to catch. The top of each plate’s range is pushed back above 1.0, weighted by luminance, restoring the headroom the file threw away. Take Punch to zero and the glass stops glowing.
-
04
thr 1.00 · knee 0.55
Bright pass with a soft knee
Everything under the threshold is discarded, everything over it rolls in gradually instead of switching on. A Karis average across each tap group stops single hot pixels from becoming flickering stars.
-
05
6 levels
Mip chain, down then back up
Six successive halvings with a 13-tap filter, then a 9-tap tent blur additively climbing back. Cheaper than one wide blur, and the falloff looks like a lens rather than a gaussian smear.
-
06
0.62
Spectral dispersion
Ten taps walking outward along the radial axis, each weighted by a smooth red-to-blue response and normalised so total energy holds. This is why the plate edges fringe like a prism instead of the usual three-channel offset.
-
07
AgX
Tone map
AgX by default: log-encode, apply a contrast curve, come back out. Its useful trait is that bright saturated colour slides toward white instead of clipping to a flat primary, which is what keeps a blown highlight looking photographed. Swap it for ACES, Reinhard, or nothing to see what it was doing.
-
08
50%
Gamut stretch
The canvas writes into a Display-P3 buffer. At 0% the sRGB-to-P3 matrix is applied properly and the page looks exactly as it would in sRGB. Above that the matrix lerps toward identity, so the same numbers get read against wider primaries — the cyans and magentas in these plates go somewhere an sRGB page cannot reach. A deliberate mis-tag, not a conversion, and that is the whole trick.
-
09
0.22
Grain and dither
Grain is weighted toward the mid-tones, where film actually holds it, and left out of the deep shadows. A quarter-LSB ordered dither underneath kills the banding eight-bit output would otherwise put across every gradient.
The seven works
Each plate is one layer of a 1024 × 720 texture array, sampled only for the pixels that land on it. Pick one and the ring turns to face it.
What is real here
Controls
Drag the ring to spin it, ← → 1–7 to jump. Click a canvas to bring it forward. Trackpads can swipe the ring sideways.
Honestly faked
There is no global illumination. The floor is a single mirror bounce with a ripple offset standing in for roughness, the plate edges are lit by a constant rather than by anything in the scene, and the HDR headroom is reconstructed from eight-bit files, not captured.
Honestly real
The half-float buffer, the bloom mip chain, the AgX curve and the sRGB-to-P3 matrix are the same maths a film pipeline uses. Nothing in the grade is a CSS filter, and no pixel is fetched at runtime.
Motion
All seven plates are stills, and nothing is drawn over them. Each sits in its own current: a velocity field taken as the curl of a noise streamfunction, so it neither piles up nor tears holes, transported semi-Lagrangian so it carries history and features stretch into filaments the way they do in a real flow. What travels is a displacement field rather than colour, so the photograph is resampled sharp every frame no matter how long it has been moving. Everything else follows from that. The material brightens where the current stretches it and shifts hue where the film it depicts changes thickness. What cannot flow is not made to: a sheet of dichroic glass is rigid, so instead a normal is lifted from its own luminance and lit by the ring’s real geometry, and its highlights sweep as it turns past the key light. Nothing is drawn on top of anything. Drag Motion to zero and the currents settle out.
Looping video was tried for the three fluid plates and dropped. A clip has to live in a smaller texture than a still and arrives already compressed, so it read softer than the plate beside it, and the motion it bought was slighter than the current it replaced — the ink in particular lost its churn. The measurement that made the loops seamless still holds, it just was not worth the resolution. They were generated with the end frame pinned to the same image as the first, so the last frame arrives back where the clip began: the measured seam is 3.0 against 91.1 for an unpinned clip, and below the clip’s own frame-to-frame motion. There is no crossfade hiding a cut, because there is no cut.
Plates
Seven stills from Leonardo (lucid-origin, 1024 × 720), seeds 114201 · 220874 · 903318 · 449302 · 551188 · 663940 · 778553. Everything is inlined — the page fetches nothing at runtime.
Performance
Render scale drops automatically if the frame rate sits under 45. Rendering stops entirely once the ring scrolls out of view, and freezes if the tab is hidden.