DENDRA Dendrochronologia
How it was made

Building a disc where every ring is one legible year.

DENDRA renders a tree-ring cross-section on a single 2D canvas — one hundred and eighty-four annual rings drawn from a deterministic climate series, growing outward from the pith and readable year by year.

01The concept

A felled Sierra pine, section 47, spanning 1840 to 2023. The page's single job is to make duration legible: to let a visitor watch a whole tree's life accrete ring by ring, then read any one season — its width in millimetres and the weather that set it.

Audience: anyone curious about how a tree records climate — students, naturalists, and the merely patient. The disc is the whole product; the surrounding copy only names its parts.

02Palette

Every colour is taken from a real cross-section — nothing decorative. The bright ochre is reserved strictly for lines and marks (the cursor ring, the shimmer); small text always uses the dark bark-ink so it clears 4.5:1 on the warm ground.

Heartwood
#6B3E24
The closed, oldest core near the pith.
Sapwood
#C99A5B
Living outer years, paler and open.
Bark-ink
#3A2417
Small text — deepest brown, max contrast.
Scar-ochre
#D9A441
Lines & marks only — the reading cursor.
Char
#241A14
The fire-scar lens sealed into the wood.
Paper
#F3EADB
Aged herbarium ground behind the disc.

03Type

Spectral carries the display and prose — a Koen Bruijn serif with a botanical-plate calm, set light (300) at large sizes for a specimen-sheet feel. Spline Sans Mono handles every reading: the year, the millimetre width, the live ring count. A monospace makes the numbers feel measured, like a lab caliper, and keeps columns from shifting as values tick.

04Technique

The disc is one <canvas> 2D context — no WebGL, no libraries. A seeded mulberry32 PRNG plus two sine bands (an ~11-year solar-ish cycle and a slower multidecadal swing) generate a reproducible width-per-year series in millimetres; fifteen named events override it — droughts pinched to a hair, wet years opened wide, three fire years drawn as a char lens on the eastern sector.

Each ring is an annulus: an even-odd fill of earlywood colour (heartwood→sapwood interpolated by age) with a darker latewood stroke at its outer edge — that boundary line is the thing you actually count. The whole stack is rendered once to an offscreen canvas; the animation frame only blits that bitmap and paints the cursor overlay on top, so reading stays smooth after growth completes.

Signature: growth is a single float grown eased from 0 to 184 over ~6 seconds; the outermost partial ring is drawn at a fractional radius and a faint ochre cambium shimmer rides its edge. Move the pointer (or press /) and the radius under the cursor is mapped back to a year — the ring lights ochre, a radial caliper line drops, and the panel reads that season's story. A second small canvas unrolls the last forty rings as a linear core, so the radial and the strip agree.

05Iteration log

Pass 1 · Craft

Drove the growth start-to-finish and read a dozen rings. Findings and fixes:

  • Latewood lines were a fixed 1px and vanished on wide rings — made the stroke scale with ring width (capped), so both broad and pinched years read as distinct rings.
  • Removed a redundant double setActive() call in start-up that briefly flashed the wrong resting year before the fire ring.
  • Tightened the type scale: hero to Spectral 300, all readings to a single mono size; dropped the ink-soft brown from the smallest labels in favour of bark-ink for contrast.

Pass 2 · Depth

Enriched the signature and added a second-read layer:

  • Added faint medullary rays — seven low-alpha radial lines — so the disc reads as sawn wood, not a target, on close inspection.
  • Built the "unrolled" core strip: the same last forty rings as a horizontal barcode, with the active year boxed in ochre in both views at once.
  • Made wider rings render slightly paler earlywood, so a broad wet year visibly glows against a pinched drought — width you can feel before you read the number.

Pass 3 · Hardening

Drove the page at 375px, under reduced-motion, and with the tab hidden:

  • 375px: the bench collapses to one column, the panel drops below the disc; nav and wordmark stay within the viewport, no horizontal scroll.
  • prefers-reduced-motion: skips the growth animation and the shimmer, drawing the finished 184-ring disc immediately with the fire ring parked lit — a settled, premium static frame.
  • Capped DPR at 2, re-layout on debounced resize, paused the rAF on document.hidden and reset the clock on return so growth never jumps.
  • Held the Chanel rule: declined a vignette and a paper-grain overlay on the stage — both fought the fine latewood lines. The ochre caliper stays the only added mark on the wood.

← back to the disc  ·  the collection