PENNY GAFF

A build note · from the workbench

How the Wheel of Life turns

A working zoetrope rendered without a single photograph — one honest strobe, one drum of thirteen slits, and the arithmetic between them.

01The concept

Penny Gaff is a fictional Shoreditch maker of optical amusements, 1867 — and the page sells one toy by making you work it.

The single job of the site is to let a modern visitor feel persistence of vision the way a Victorian child did: spin the drum too slowly and the picture judders into separate slaps; spin it in the true band and thirteen drawings fuse into one running dog; spin it too hard and the figure ghosts a pale twin half a step behind. Nothing is faked or eased into looking right — the strobe either earns the illusion or it doesn't, by your hand. That honesty is the pitch.

02Palette

Argued straight from a red tin drum on a wooden bench under gaslight — not a "tasteful" scheme dropped on top.

Circus dark
#221A1E
A warm near-black with red-brown in it — a canvas tent at night, never a cold neutral.
Drum red
#B3342B
Carriage-red enamel on rolled tin. The one loud colour, spent only on the drum and the shout.
Brass
#C9973E → #EBCD86
Rims, seams, the gauge needle. A metal graded light-to-dark so edges catch a highlight.
Playbill cream
#EFE2C4
The paper of the strips and the body of the type — aged, not bleached office white.
Spectre violet
#B9A8D6
Reserved for one thing: the hidden fourteenth strip that arrives when you spin too hard.

03Type

The Wheel of Life
Rye · display

A Tuscan slab straight off a Victorian playbill. Used only for the wordmark, headings and the gauge zones — one line of it earns a room; a paragraph of it would shout down the physics.

Thirteen frames, thirteen slits.
Lora · text & italic

A quiet transitional serif for every word you actually read. Its italic does the copywriting voice — the maker talking across his counter — while Rye keeps its distance.

04The signature — an honest strobe

There is no video and no sprite sheet playing back. Each strip is a single wide canvas of thirteen hand-drawn frames (the dog, pony and tumbler are drawn live with parametric limbs, so every pose is computed, not stored). On every animation frame the code walks the drum's thirteen slits, and for each slit currently facing you it samples the one thin vertical band of the strip visible through that gap and paints it into a small offscreen retina buffer.

That buffer is the trick. It doesn't clear each frame — it fades by 1 − e−dt/τ with τ ≈ 0.09 s, the eye's real persistence. The rotation between two frames is sub-stepped, so a slow drum leaves dark gaps between exposures (judder), the true band lands each new flash inside the last afterimage (fusion), and a fast drum crowds two or three frames into one glow (ghosting). The band is then composited onto the drum through a curved-slice warp so it sits inside the elliptical wall. The drum itself carries real angular momentum with combined viscous and Coulomb friction; a flick reads its speed from the last ~110 ms of pointer history. The gauge isn't decoration — it reads the same omega the illusion depends on.

05Techniques, in short

06The three passes

Pass 1 — Craft

Found: the intro instruction bar under the drum was set white-space:nowrap, so on a 375px phone its single line ran to 401px and pushed the page sideways.

Rewrapped it, capped its width to the drum and gave it a comfortable line-height so it now breaks to two centred lines on narrow screens. Read the copy and type scale end to end and left the disciplined parts alone.

Pass 2 — Depth

Found: the gauge told the truth in words but not in feel, and nothing invited the very first spin.

Tied the zone word to the physics — it now takes a warm brass halo the moment you cross into TRUE and a red one in GHOST, a reward you only notice on the second look. Added a slow brass "invite" pulse to the Give it a flick button that stops for good the instant you touch the drum. (The hidden Spectre strip that appears after spinning too hard was already the deeper second-read reward.)

Pass 3 — Hardening

Found: a stubborn 18px horizontal scroll at 375px that no element's bounding box explained.

Traced it to the drum's halo — a ::before glow inset -10% past the drum on each side — and to a legacy visually-hidden helper whose clipped nowrap text leaked into the layout. Fixed both; the page now holds exactly 375px with zero console errors. Removed one repeated fleuron from the playbill (the Chanel rule — take one thing off), and re-checked focus rings and the reduced-motion path, which renders a still, legible drum.

Now go and spin the drum, or return to the Generative Assets index.