This page renders its gallery with WebGL2, which this browser did not provide. Everything below still reads — the ring is the only part that needs a GPU.

Photic Rotunda
Output Grade AgX Loading plates

Photic Rotunda

Seven photographs of colour being made by matter — dichroic glass, oil on water, cut crystal, anodised titanium, magnetised fluid, ink, foil. They hang on a ring in a dark room, raycast in a single fragment shader, each one stirring under a motion signature matched to what its subject would actually do — and the grade that lights them comes apart on the right.

Drag the ring  ·  scroll for the chain
Grade engine – fps
0.25
0.80
5.5
0.85
0.62
1.12
0.50
0.22
1.00
Tone map
Preset

Render scale 100% · 0 × 0

Chain

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.

  1. 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.

    – × –
  2. 02

    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.

    0.85
  3. 03

    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.

    ×6.5
  4. 04

    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.

    thr 1.00 · knee 0.55
  5. 05

    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.

    6 levels
  6. 06

    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.

    0.62
  7. 07

    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.

    AgX
  8. 08

    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.

    50%
  9. 09

    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.

    0.22
Index

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.

    Notes

    What is real here

    Controls

    Drag the ring to spin it, 17 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.