Field guide · How it was grown

Notes from
the glass wall

RHIZOTRON is a fictional root observatory: a single taproot grown against glass while water is buried where it cannot see it. The page's one job is to make a viewer feel a plant deciding — leaning toward moisture, refusing a stone, and paying carbon only into the routes that work.

01Palette

Argued from wet soil, not chosen for taste

Everything is drawn on the dark, so the roots read as living ivory against earth and the only chromatic note is the water itself. The accent teal is kept for marks and light; body copy runs on a warmer ivory ink so it clears 4.5:1 over the soil.

Deep earth
#1A130C
The page ground. Near-black with a brown bias so it reads as soil, not UI dark.
Topsoil
#3B2A1C
Warm crumb at the surface; the gradient top and quiet panels are built from it.
Root ivory
#EDE4D0
The roots and all body text. A living bone-white, never pure #FFF.
Water pocket
#4A8FA0
The one hue in the ground. Reserved for buried water, its glow, and rule marks.
Water, lifted
#7FC0CE
A brighter teal used only for small teal text, so labels stay legible on dark.
Ivory ink
#B2A489
Muted secondary type. Carries the reading load without shouting over the roots.

02Type

Fraunces + Inter

Display
Rhizotron

Fraunces, at its highest optical size and heaviest weight, gives the wordmark a botanical, almost horticultural-plate feel — old growth, not tech. Its italic carries the single verbs the page turns on: decides, earns.

Text & data
96 cm deep

Inter handles copy and the live readout, with tabular numerals so the depth, lateral and pocket counters never jitter as they climb. Neutral where Fraunces is expressive.

03Technique

SVG · space colonisation · no libraries

The whole page is hand-written HTML, CSS and vanilla JavaScript — no framework, no canvas, no external library. The soil is a single inline SVG; the roots are ordinary <line> elements appended one at a time as the plant grows, so every strand is real DOM you could inspect.

The signature — a root that decides. Moisture is modelled as thousands of invisible attractors seeded through the soil: a faint downward corridor that keeps the taproot descending, plus dense clusters wherever water is buried. Each tick, every growing tip gathers the attractors within a 15 cm reach, averages the direction to them, adds a gravitropic downward pull, and extends a fixed 8 mm — the Runions space-colonisation model. Attractors within a kill radius are consumed and vanish, which is why a tip commits to water and abandons dry ground; where several attractors pull at once, the path forks into a lateral. The buried stone deletes the attractors inside it and deflects any tip that touches its face, so roots slide around it the way pressure-sensing caps do. Once growth settles, a reverse pass counts how many tips each segment feeds — flow — and paints stroke-width from it, so productive routes visibly thicken while dead-ends stay hair-thin. Reach a pocket and a bead of moisture wicks back up the winning path to the crown.

Performance stays honest: a spatial hash keeps the nearest-tip search cheap, new growth is capped per frame for a steady draw, the loop pauses on document.hidden, and prefers-reduced-motion grows the whole system in one synchronous pass and presents a settled, static plot.

04Iteration log

Three passes, actually walked

Pass 1Craft

First growth put both default water pockets where the taproot could reach only one — the second sat unreached in the corner, reading as a bug rather than a choice. Repositioned the scene so a lateral must steer around the stone to reach the right pocket, demonstrating both tropisms in one frame. Reached pockets had been fading to near-invisible; reworked them into legible damp teal stains. Boosted the thickening contrast (thin tips ~1 px, fed trunk ~8.8 px) so "routes that pay off" actually reads. Removed the stone's hard facet triangle — it looked like cut paper — for a single soft highlight.

Pass 2Depth

Enriched the signature with a second-read detail: once the plant settles, fine absorptive root hairs sprout along the established, thickened roots — invisible at a glance, noticed on a closer look. Narrowed the moisture corridor so a single dominant taproot leads instead of two parallel stems, truer to the botany. Added the moisture wick that travels up a route the moment it reaches water, and a hover reticle so burying a pocket feels aimed. Retuned growth pacing to a steady, watchable draw.

Pass 3Hardening

Caught and fixed a real defect the "no errors" probe had masked: an array-grows-during-iteration bug that grew tips from uninitialised nodes (silent NaN coordinates), and a requestAnimationFrame timing edge where the first frame's timestamp predated the captured start, sending the wick to pts[-1]. Reworked the mobile controls into a single compact row and put the live readout on one line; dimmed the depth ruler so it sits behind the data. Verified 375 px has no overflow and the wordmark and nav sit inside the viewport, reduced-motion renders a full static plot, and the settle loop always comes to rest.