THERMAL.

Build note · Second Person · No. 79

Heat-developed
type

THERMAL is a cold thermographic plate. Its headline is printed in a temperature you can’t read — until you warm the surface with your cursor. A real heat-diffusion simulation blooms and cools in your wake, and the letters surface only where the field is hot. Move away and it forgets. This note covers how that was built.

The concept

A Wave 4 “Second Person” piece: the page is inert until the viewer drives it. Here the viewer is the heat source. There is no reveal button and no autoplay — the only warmth in the room comes from the visitor’s cursor. The single job of the page is to make you realise that the words exist only because you warmed them, and that the surface won’t hold them for you.

Palette

A true thermographic (iron) ramp — cold at the bottom, white-hot at the top — because the colour is the data: every pixel’s hue is its temperature. Text gets its own warm ink tokens so captions stay legible over the plate.

Cold
#0B1E3A
Ambient plate. Deep navy, near-black — the temperature of “never been here”.
Mid
#7A2E8A
Violet — the arriving edge of a fresh stroke, warmer than air.
Hot
#FF5A2C
Where you lingered. Reserved for lines & marks, never small text.
White-hot
#FFE9B0
The pressed core, and the ink the letters develop into.
Ink
#EFE6D6
Warm off-white for headings and the live HUD — reads on the dark plate.
Ink-dim
#AEA595
Body copy. A cooled bone tone, calm against the warm ramp.

Type

You Are The Heat
Archivo — 900, width 125 (expanded). The headline is heavy and stretched so its wide strokes hold the most warmth: broad letterforms give the diffusion field a bigger surface to develop, so words read clearly even at low heat.
18.0 °C · surface at cursor
JetBrains Mono — 400/500/700. Every reading, hint and eyebrow is monospaced — this is an instrument, and the fixed grid makes the live temperatures feel measured rather than decorative.

How the signature works

The plate is a GPU heat-diffusion simulation. A single-channel temperature field lives in a floating-point texture. Each frame:

1 · Deposit

A seed shader adds a Gaussian of heat at the cursor’s position (read from pointer events, converted to the field’s UV space). Dwell and it climbs; dash and it barely registers.

2 · Diffuse & cool

A sim shader runs an explicit finite-difference step of the heat equation — a four-neighbour Laplacian spreads warmth to adjacent cells (D ≈ 0.16), while a linear term leaks every cell back toward ambient (cooling half-life ≈ 1.4 s). It runs three sub-steps per frame for stability. The two textures ping-pong: read from one, write to the other, swap.

3 · Develop the type

The headline is rasterised once to an offscreen canvas and uploaded as a mask texture (main line in the red channel, a hidden line in green). The display shader maps temperature through the iron ramp for the background, then reveals the letters only where the field is warm — smoothstep(0.09, 0.40, T) — brightening them from hot orange toward white-hot ink as the spot heats. Cool the spot and the reveal fades with it.

4 · Read it back

The live HUD is not scripted. A 1×1 probe pass samples the field at the cursor and readPixels pulls the value to the CPU, which scales it to a thermal-camera °C range. That’s the number in the corner and in the readings panel.

If WebGL is unavailable, a canvas-2D fallback runs the same maths on a coarse CPU grid and reveals the text with a destination-in composite. With reduced-motion or no JavaScript at all, the headline is real HTML text on a static thermographic gradient — the copy is never permanently invisible.

Techniques

Iteration log

Pass 1 · Craft

Caught the worst bug first, and it was invisible to a naïve probe: the heat field bloomed perfectly and the temperature readings climbed, but the headline never appeared. The text mask texture was created only inside the upload routine, which the first mask build skipped — so the display shader sampled a null texture and the letters were always dark. A “silent dead signature”: every metric passed while the actual point of the page did nothing. Fixed the upload order, then split the accent into a line/ink token pair — hot #FF5A2C for rules and marks only, warm off-white #EFE6D6 for text — so the HUD holds ≥ 4.5:1 over the plate, and balanced the iron-ramp thresholds so violet and orange read as distinct bands.

Pass 2 · Depth

Reworked how the type surfaces: instead of dark cut-outs, letters now render one temperature band hotter than their surroundings, so they glow up out of the warmth as it arrives. Added the live cursor probe so the °C readings are real — the number you watch is the exact value under your cursor, read straight off the sim. Rebuilt the intro into a 7-second development: a demo comet teaches the trailing reveal, then the whole title fills in evenly and holds — so the plate is alive and legible at the moment its preview is captured, before it cools and hands over. Buried the second-read detail: a hidden line, “— and then you are gone”, that only surfaces where you push a spot to white-hot.

Pass 3 · Hardening

Drove the cursor in headless Chrome across three passes at 1280 / 768 / 375 and asserted the field warms and the text develops each time — zero console errors, no overflow. Two defects only the screenshots caught: at 375 px the long words bled off both edges, fixed with measure-based fit-to-width sizing; and the reduced-motion poster over-saturated its centre while its note collided with the readout — reworked to an even development with the HUD hidden. Guarded the known FBO-viewport pitfall (a stray viewport from the 1×1 probe flat-lining the sim) by setting the viewport inside every draw pass. Capped DPR at 2, debounced resize with a full sim + mask rebuild, paused the loop on visibilitychange, and verified the canvas-2D fallback and the no-JS static headline both stay legible.