How Hearth was baked.
Hearth runs on one photograph, one five second loop, and a lot of drawing. Everything else on the page, from the bread on the menu board to the starter crock, the oven and the dividers, is hand written SVG, canvas, and CSS. This page walks through the seven systems that do all the work, so you can steal every one of them.
Butter, rye, and one good rust.
Every color is something you could find on the counter. If a hue has no job, it does not get a seat.
The page alternates between two worlds: a cream world (flour dusted paper, dark rye ink) and a board world (the chalk-dark menu board, cream text). Rust is the crust and the call to action; wheat gold is oven light; sage shows up only where herbs would. That is the whole cast.
A literary voice against tiny discipline.
The taste lives in the gap between the biggest and smallest type on screen.
Bread, slowly.
DM Serif Display
The display voice. A little old menu board, a little novel. It only ever appears at conviction scale (the hero sets it at up to 11.4rem), and italics are reserved for the word that matters.Forty eight hour ferment.
Nunito Sans
Every paragraph, price, label and button. Warm, round, unremarkable on purpose: it carries information while the serif carries feeling.Three fixed voices: display, italic accent, and 0.72rem caps labels tracked at .22em. Nothing in between is allowed to exist.
Seven systems doing all the work.
Learn these and you can rebuild the whole bakery, or your own version of it.
Staging the photograph, not placing it
The hero is one 2560px photograph, but nothing about it is passive. It wears a grade stack: a warm multiply wash, a top and bottom scrim so chrome and type always sit on quiet pixels, an ember glow, and the site wide grain.
The trick that makes the rest possible: the loaf's position is stored in the photograph's own pixel space and projected to screen coordinates on every resize, using the same cover crop math as the browser. Steam, glow and the crust mask always know exactly where the bread is, at any viewport.
/* the loaf, in the photo's natural pixels */
var LOAF = {cx:1090, cy:700, rx:560, ry:380};
/* same cover math as object-fit */
var s = Math.max(vw/NAT_W, vh/NAT_H);
var ox = (vw - NAT_W*s) * posX;
hero.style.setProperty('--loafX', (LOAF.cx*s+ox)+'px');
The bake engine: one clock, many hands
The signature moment. A second copy of the hero photo sits on top, filtered toward deep crust and masked to an ellipse over the loaf. One 24 second clock drives its opacity, so the crust visibly deepens, then resets when the tray leaves.
The same clock feeds everything else: the ember glow under the loaf, steam at the crown when the bake finishes, the oven readout's time, dome temperature, stage label and progress dial. One number in, a whole scene out. That is why it never feels like separate gimmicks.
/* p runs 0 to 1 over 24s; windows via smoothstep */
var crust = smooth(p, .15, .68);
var fire = smooth(p, .05, .34) * (1 - smooth(p, .7, .96));
var steam = smooth(p, .7, .8) * (1 - smooth(p, .92, .99));
hot.style.opacity = crust * .92; // masked twin photo
ember.style.opacity = fire * .75;
readout.set(p); // clock, dome, stage, dial
Flour motes that know where the light is
A capped canvas particle field drifts down and right with a slow sine sway. Each mote's brightness is a function of its distance from the window's light shaft, a line segment also stored in photo space, so dust twinkles where the sun actually falls and almost disappears in the shadows.
Thirty motes on phones, sixty four on desktop, device pixel ratio capped at two, and reduced motion gets a single hand placed frame instead of a loop.
/* distance from the shaft's center line */
var t = ((m.x-ax)*dx + (m.y-ay)*dy) / len2;
var d = Math.hypot(m.x-(ax+t*dx), m.y-(ay+t*dy));
var inShaft = 1 - Math.min(d / shaftWidth, 1);
alpha = m.base * (.18 + inShaft * .95);
The trays, photographed in one light
The menu board shows the real morning: three overhead trays shot on the same bench, in the same raking window light, with the same warm grade. Picking a bread crossfades to its tray and settles with a soft scale, like a plate being set down in front of you.
Consistency is the whole trick: one light direction, one grade, one camera height across every food photograph on the page. House law, learned the hard way: a drawn object is a defect. If it depicts a real thing, it is a photograph or it is gone.
.stage__item { opacity:0; transition:opacity .5s; }
.stage__item.is-on { opacity:1; }
@keyframes trayIn {
from { transform:scale(1.05); opacity:.35; }
to { transform:scale(1); opacity:1; }
}
A century pinned to one scroll
The starter's hundred years live in a pinned viewport: the section is a tall runway, the scene sticks, and scroll progress maps linearly to the year counter and the era stories, while the real jar glows beside them in the window light. Scrubbed motion gets no easing, because easing on a scrub reads as lag.
One hard rule learned on this page: era windows are disjoint. Each story fades fully out before the next fades in, so two paragraphs can never share a frame mid scroll. Crossfading text always overlaps eventually; separated windows never do.
var gap = 1/(n-1);
var half = gap*.5 - .012; // ends BEFORE the midpoint
var o = clamp((half - Math.abs(p-c)) / (gap*.24), 0, 1);
o = o*o*(3-2*o); // smoothstep the edges
era.style.opacity = o; // neighbours never coexist
The slam: the take plays whole, and nothing touches it
At five a.m. the baker kneads, lifts the dough high and slams it down in a cloud of real flour, then pats, folds and rolls the loaf. The film is the whole take, untouched: no zoom, no grade pulse, no particles drawn over it, no dissolve, no speed ramp. The flour you see flying is the flour that flew. The only edit is invisible by design. The loop begins and ends on two resting frames, so when the film returns to its start, nothing on screen is moving.
Restraint is the system: when the footage already carries its own effect, the page adds none on top. Around the film the room still breathes: ambient flour specks drift the whole page, warm over the dark boards, rye-shadow over the cream. But the frame itself is sacred.
// the film carries its own flour; the page draws nothing over it
// the loop's two end frames are the footage's calmest moments,
// so the wrap lands on stillness
<video muted playsinline loop>
<source src="the-whole-take.mp4">
</video>
The seams: waves, grain, buttons
No section ends in a straight line. One lumpy dough wave, stretched with preserveAspectRatio="none" and refilled per section, hands each color world to the next. Repetition reads as a motif, not a template.
A single SVG turbulence filter lies over the whole page at 40 percent multiply: the flour dust on every surface. And every button is the same designed object: 3px radius, ink border, a fill that sweeps in from the left while the label rolls up. One radius, one easing curve, one shadow logic, everywhere.
<feTurbulence type="fractalNoise"
baseFrequency="0.82" numOctaves="2"/>
.btn::before { transform:scaleX(0); transform-origin:left; }
.btn:hover::before { transform:scaleX(1); } /* the sweep */
Bake your own in six steps.
The order matters: the verb first, then the light, then the drawings.
Pick one physical verb
Baking, brewing, pouring: something with a visible before and after. Hearth's whole page orbits "the crust deepens." Your signature moment is that verb, staged.
Shoot one hero that owns the light
One photograph with a clear light source beats a gallery. Store your subject's coordinates in photo space so every effect can find it at any crop.
Drive the scene with one clock
A single 0-to-1 progress value feeding mask opacity, glow, particles and an instrument readout keeps ten effects feeling like one event.
Shoot everything in one light
One light direction, one grade, one camera family across every photograph. A consistent set reads as a place; mixed lighting reads as stock. And never draw a real object: photograph it or cut it.
Pin one story, keep windows disjoint
One scrubbed passage per page is plenty. Map scroll linearly, give every beat its own exclusive window, and keep the pin under a handful of viewports.
Seam the sections, dust the page
One divider motif at every boundary, one grain filter over everything, one button object reused. Coherence is what premium actually looks like.
What actually built this.
No build step, no framework, no libraries. View source on the site: it is all there, in one file.