How this site was built
Build guide · lot 26-07-EG
The concept
Torrefaction No. 4 is a single-origin roastery obsessive about roast curves, pitched at specialty-coffee nerds, with one job: get you to subscribe to the monthly drop. There are no photographs anywhere — the color science and the curve are the visuals. The page reads as a process narrative, cherry to cup, because coffee genuinely is a sequence and the numbering encodes real order.
Palette
Everything is mixed from the roastery’s own materials: kraft bag paper, espresso ink, a copper drum, green beans before the charge.
The bean swatch in the readout is not a fixed color: it is interpolated live between seven measured roast-color stops, from cinnamon tan (#C8A165) to French near-black (#241108).
Type pairing
Alfa Slab One — the stencil on the bag.
Archivo carries the body copy: sturdy, neutral, a workshop sans that stays out of the slab’s way.
IBM Plex Mono · net wt 250 g · lot 26-07-EG · axis labels & bag vernacular
Alfa Slab One is used the way a roastery uses a stencil — rarely, and big. Archivo does the reading. IBM Plex Mono does everything that would be printed by a machine: axis ticks, lot numbers, chips, buttons. Three faces, three distinct jobs.
The signature: a draggable roast curve
The chart is one hand-built SVG, no chart library. A real-shaped bean-temperature profile (charge 204 °C, turning point 96 °C at 1:30, first crack 196 °C at 9:00) is defined as anchor points and made smooth with Catmull-Rom interpolation into a dense lookup table:
var ANCHORS = [[0,204],[0.6,146],[1.5,96],[3,118],
[4.5,138],[6,157],[7.5,175],[9,196], /* first crack */
[10,203.5],[11,210.5],[12,217],[13,224],[14,230.5],[15,236.5]];
// Catmull-Rom → SAMPLES[], then tempAt(t) is a linear scan + lerp
The drop handle is an SVG group with role="slider". Dragging converts pointer
x-position back into minutes, clamps it to the development window (9:24–15:12), and re-renders
the solid curve, the shaded development band, and every readout:
function tFromClientX(clientX) {
var rect = svg.getBoundingClientRect();
var sx = (clientX - rect.left) / rect.width * W; // CSS px → viewBox
return clamp(X0 + (sx - M.l) / (W - M.l - M.r) * (X1 - X0),
T_MIN_DROP, T_MAX_DROP);
}
Roast level, Agtron number, flavor notes and brew method come from an honest lookup table of drop-temperature ranges (City at 201–208 °C, Vienna at 227–234 °C, and so on). The bean color is a keyframed RGB interpolation over drop temperature, and an “oil sheen” highlight fades in past 226 °C — right where second crack starts pushing oils to the surface:
var sheen = clamp((temp - 226) / 10, 0, 1);
beanSheen.style.fill = "rgba(236,223,196," + sheen * 0.35 + ")";
Three console-style details make the drag feel like operating a machine: a live cursor chip on the temperature axis that tracks the handle, a darker zone that floods in once the roast crosses second crack at 224 °C, and a dashed “July pick · 211” ring that appears the moment you leave the house roast — so you always know how far you’ve wandered from the bag we actually ship.
The curve draws itself on first scroll-in via the classic
stroke-dasharray/-dashoffset trick, skipped entirely under
prefers-reduced-motion. Arrow keys nudge the drop point 6 seconds at a time
(Shift for 30), so the whole instrument works without a mouse.
The three passes
- Pass 1 — correctness & composition The screenshots caught the lab and stations invisible before scroll — a fragile scroll-reveal system got deleted outright. Fixed the 390 px header overflowing horizontally (nav now wraps to its own row), and redrew the cherry and drying-bed glyphs that read as spectacles.
- Pass 2 — elevate Added the rate-of-rise trace under the main curve (real roasting consoles show both), a live cursor chip on the temperature axis, the second-crack zone that floods in past 224 °C, the “July pick” reference ring, a stamp animation when the roast-level name changes, the printed batch-card in the hero, and hover states on the six stations.
- Pass 3 — taste Data honesty: the default drop point lands exactly on the advertised July roast (211 °C, Agtron 62) and every number on the page agrees with it. The second-crack label now yields when the handle passes over it, the drop tag stays inside the plot at full French, the batch-card ships on mobile too, and the drag hint dismisses itself after first use.
Do this yourself
- Pick a subject with real data. The site works because roast curves are honest: fixed physics (first crack) plus one human decision (the drop). Find that structure in your subject.
- Mix the palette from the subject’s materials. Kraft paper, espresso ink, copper drum, green bean — name each hex after a thing you could touch.
- Give each typeface one job. A display face used rarely and big, a body face that stays quiet, a mono for anything a machine would print.
- Build the signature by hand. Ask Claude for a raw SVG chart — axes, units, real ranges — not a chart library. Hand-built means the interaction can be exactly what the subject needs.
- Make the interaction change everything at once. One drag should move the curve, the color, the numbers, and the words together. That’s what makes it feel like an instrument instead of a widget.
- Write like the subject’s packaging. Bag-label vernacular — “NET WT 250 g, LOT 26-07-EG” — is free design material hiding in every niche.
- Screenshot, critique, repeat — three times. Look at desktop fold, full page, and 390 px. Each pass must change something; the last one should mostly remove.
- Floor-check before shipping: keyboard focus visible, reduced motion respected, no console errors, nothing overflowing at 375 px.