Build note · Wave 4 · Second Person
FROST is a cold pane between you and a winter night. It is inert — a blank sheet of fog and fern-crystal — until your pointer moves across it. There is no autoplay and no reveal on load: the scene behind exists only where your breath and fingertip have been, and the cold grows the frost straight back over your smear. This note explains how the pane is actually built.
A fictional winter reading for a single late-January night. The audience is anyone who will slow down for thirty seconds. The page has exactly one job: make you feel that you are the only warmth the window has left — that nothing arrives unless you reach for it, and that what you reach for cools and closes the moment you stop.
It sits in Wave 4, "Second Person": the page addresses you and cannot exist without your hand. Move away and it stills, fogs, and re-crystallises, waiting to be earned again.
Cold blues for the glass and the night; a single warm ember for the hearth you are trying to reach. The warm accent is reserved for light and marks — text keeps a legible ink.
The night is not black. The snow is lit by a gibbous moon, so a wipe reveals a luminous blue winter rather than a void — which is what makes the small amber window read as warmth and not just a light. Per the collection's ink/line rule, the hearth orange is only ever used for light and hairline marks; body and label text use a cooler ink (#AFC5D0 / #C6D6DF) so nothing small drops below 4.5:1.
Fraunces, italic, optical-size on. Its soft, old-style italic has the hand-cut warmth of a hearth and a bookish, wintry quiet — the opposite of the cold pane, which is the point.
Inter, 300–700. Neutral, legible at every size, and calm enough to disappear so the pane is the only thing performing. Eyebrows and labels use its wide tracking.
A real fog-and-frost buffer the pointer clears, composited over a procedural winter scene. No images, no shaders — one 2D canvas and a grid of numbers.
The pane is driven by two floating-point fields on a coarse grid (one cell per ~7 CSS px, ~21,000 cells at desktop size): fog — the soft breath-mist — and ice — the crystalline fern-frost. Both are 1 (fully frosted) at load.
Your pointer is a brush. Each move stamps along its path, subtracting from both fields with a smooth radial falloff. The two clear at different rates on purpose: one soft pass wipes the fog almost completely (you see through immediately), but the ice barely yields — the ferns only melt where you dwell or press, so scrubbing a spot is how you truly clear it. A fast swipe reads as breath; a slow rub reads as a fingertip.
Every frame, a simulation grows the frost back. Fog regrows toward 1 and diffuses laterally, so holes softly refill from their edges within a few seconds. Ice re-crystallises far more slowly, nucleating and then growing inward from frosted neighbours, capped by a baked fern-field so the crystals return along the same veins — it re-crystallises in place rather than smearing.
Rendering is a mask composite: the scene is drawn first, then a baked fog-veil texture and a baked fern-crystal texture are each pulled onto the pane through a grid-resolution alpha mask built from the two fields (destination-in with bilinear upscaling for soft edges). The winter scene — cabin, glowing hearth window, moonlit snow, drifting chimney smoke, pines, a gibbous moon — is entirely procedural canvas 2D, baked once per resize; only the falling snow and a subtle hearth flicker animate behind the frost.
It is guarded: DPR capped at 2, canvases sized to the container and rebuilt on resize, the loop paused on document.hidden and when the hero scrolls out of view, and a settled static frame under prefers-reduced-motion. If the canvas fails, the CSS-gradient pane and all copy remain — the words are never hidden behind the frost.
Drove the cursor headlessly and read the buffer back rather than trusting the screenshot. The fog regrew too fast (0.34/s) — the reveal felt frantic and wouldn't hold — so I slowed it to 0.24/s for a graceful few-second fade. A single soft wipe also wasn't clearing enough to see through, so I raised the breath's fog-clear from 0.62 to 0.85 while keeping the ferns resistant. A local sample-at-cursor probe then confirmed one pass drops fog from ~0.80 to ~0.10, and that it climbs back to ~0.80 after you leave.
The first reveal was a let-down: wiping exposed near-black sky because the cabin was small and low. I rebuilt the scene — a larger, centred cabin with a glowing amber hearth window, brighter moonlit snow so a wipe reveals winter and not a void, a warm light-trapezoid spilling onto the snow, a drifting chimney-smoke wisp, and flanking snow-dusted pines. The entrance "breath" now sweeps across the lit window, so the first thing you glimpse is the warm house. Added a per-frame hearth flicker — a second-read detail you only notice once the window is clear.
Caught a contrast trap: the hero eyebrow was mid-steel over pale frost (~1.6:1). Lightened it and deepened the top/bottom scrims so all hero copy holds ≥ 4.5:1 whether the pane is frosted (light) or wiped (dark). Removed two corner framing boughs that were invisible under the frost (the Chanel rule — take one thing off). Added an IntersectionObserver that pauses the render loop when the hero scrolls away. Re-verified the reduced-motion path renders a static, pre-cleared cabin with no autonomous motion, and that the 375 px layout keeps the wordmark, nav and headline inside the viewport with zero overflow.