Back to the bakery
·The brief, plainly

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.

Stack · HTML + CSS + JS, one file Frameworks · none Photographs · one, plus one loop
01The palette

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.

#FBF3E4
Creamthe field, 60 percent of everything
#3B2A1A
Rye inkevery letter in the cream world
#2A1C10
Boardthe dark rooms: menu, oven, finale
#C96F2E
Crust rustCTAs and index numerals, spent like money
#E0B14C
Wheat goldoven light, italics, live accents
#8A9B6E
Herb sagerosemary and one chart marker, nothing more
02The type

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.
Bread slowly Village bakery · est. 1925

Three fixed voices: display, italic accent, and 0.72rem caps labels tracked at .22em. Nothing in between is allowed to exist.

03The systems

Seven systems doing all the work.

Learn these and you can rebuild the whole bakery, or your own version of it.

System one

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');
System two

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
System three

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);
System four

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;   }
}
System five

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
System six

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>
System seven

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 */
04Replicate it

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.

05The pantry

What actually built this.

No build step, no framework, no libraries. View source on the site: it is all there, in one file.

GalloWave Hand written HTML / CSS / JS Inline SVG illustration Canvas 2D particles One photograph + one loop Google Fonts, one request