RIFFLE is a fictional teaching piece for anyone who has ever wondered how many shuffles a deck really needs. It deals fifty-two cards in perfect sequence, then riffle-shuffles them in real time — cut, interleave, settle — while a single readout tracks how much order is left.
Its one job: to make the Bayer–Diaconis theorem physical. Seven riffle shuffles are enough to randomise a deck, and no fewer will do. You watch a clean colour gradient come apart into noise, and a rising-sequences count climb its doubling curve up to the flat line of pure chance.
Every colour is argued from a card table under low light. The cards themselves carry the whole story: each of the fifty-two is tinted along a ramp from ivory at rank one to suit-red at rank fifty-two, passing through the tan of a worn card edge. Sorted, the deck is a smooth sweep of that ramp; shuffled, it is speckled noise. Order and chaos are literally a colour gradient breaking up.
Following the collection's ink/line rule, the bright edge tone #C9B79A is used only for lines and marks. Body copy takes the ivory #EDE6D6, which clears 4.5:1 on the dark baize, with a muted #C4BBA6 for secondary lines of text.
The scene is built with React Three Fiber — the declarative React renderer for three.js — loaded through an ES-module importmap from esm.sh, with no build step. The fifty-two cards are a single instanced mesh via drei's <Instances> / <Instance>, each card holding a fixed colour from the rank ramp; only positions animate. Markup is written with htm tagged templates bound to React.createElement, so there is no JSX transform to compile.
Each shuffle runs the true Gilbert–Shannon–Reeds riffle model. The deck is cut binomially — every card is assigned to the top or bottom packet by a fair coin — then the two packets are interleaved by dropping the next card from each with probability proportional to how many it still holds. Cards lift, split into two depth planes, and cascade left-to-right into their new slots.
The meter counts rising sequences: maximal runs of cards still in ascending order. A sorted deck has exactly one; a uniformly random deck has about twenty-six, i.e. (n+1)/2. Each riffle roughly doubles the count, and the sparkline plots that doubling curve saturating against the uniform plateau near the seventh shuffle — the theorem, drawn live. After seven, the deck re-deals into its clean gradient and the demonstration begins again.
Everything is guarded to the collection's quality floor: device-pixel-ratio capped at two, automatic resize, the render logic paused when the tab is hidden, a WebGL context-loss and no-WebGL fallback, and a settled static frame — the deck parked mid-third-shuffle — when the visitor prefers reduced motion.
Verified the R3F importmap first: it needed explicit react/jsx-runtime and react/jsx-dev-runtime mappings before drei would resolve — added, and the stack then loaded with zero console errors, so RIFFLE ships as true React Three Fiber, not the vanilla fallback. Collapsed a duplicate <h1> to one, fixed a subject–verb slip in the hero copy, and rebuilt the sparkline labels so the plateau line and the 0–7 axis stopped colliding.
Stabilised the composition — the auto-orbiting camera was dragging the deck across the headline, so it was pinned to a fixed three-quarter angle with a left-hand scrim protecting the copy. Gave the shuffle a left-to-right cascade stagger so cards settle in sequence like a real riffle instead of moving in lockstep, and thickened the cards for more body under the key light. Second-read reward: the reset visibly re-deals the noise back into a clean colour gradient.
Confirmed a clean 375px layout with no overflow, the panel reflowing full-width; verified the reduced-motion path renders a genuinely static settled frame (two screenshots hash-identical); checked focus-visible states and the auto-fit that scales the deck to any viewport. Chanel cut: removed the redundant masthead sub-tag so the wordmark and eyebrow carry the header alone.