HALIDE

Colophon · How it was made

Developing a print
in the browser.

HALIDE is a fictional community darkroom in the basement of a Fremantle woolstore, for photographers who print by hand and anyone curious about the craft. The page has one job: let you develop a real print — expose a sheet, drag it through developer, stop and fix, and watch a latent image come up under your cursor — with chemistry that has genuine consequences. This is the making-of.

01

The idea

A darkroom is the rare place where an image is literally latent — already present in the paper as struck silver, invisible until the chemistry calls it up. That physical fact is the whole interaction. Nothing here is a picture of a darkroom; the trays are a live per-pixel chemical simulation, so the same act that develops film in a tank — rocking the tray so fresh developer reaches the emulsion — develops the image on screen under your hand.

The single job: rock the tray, and watch the image arrive where you rock it — early is thin, late is fogged, and there is no undo.
02

Palette — a room lit by one safe colour

Photographic paper is blind to deep red, so a darkroom is lit by a single long-wavelength safelight. The whole interface obeys that constraint: it is genuinely the colour the room would be, not a "dark theme".

Safelight room
#210505
The base. A near-black flooded with red — what your eyes actually adapt to after ninety seconds in the dark.
Safelight red
#C42B2B
The lamp itself. Borders, rules and the wet-tray edges — the one hue paper can't see, so the room can.
Enlarger amber
#E8A13C
The timer glow and the printable window. The only warm that isn't red — reserved for actions and "you're in the good zone".
Fibre paper
#F2E6D8
Body text and the print base. Warm, not clinical white — the tone of real fibre-based paper.
Dimmed copy
#D2A090
Secondary text — paper white pulled down under the red so it recedes without dropping below readable contrast.
03

Type — instrument and label

Space Grotesk
Display & body

A grotesque with a faintly mechanical, engineered feel — it reads like the silkscreened lettering on darkroom equipment. Set large and tight for the headline, it carries the one moment of typographic bravery: already there in outline only, an image not yet filled in.

IBM Plex Mono
Readouts & labels

Every number the room measures — the bath timer, chemistry ratios (1+9, 1+4), tray names, the development targets — is monospaced. It turns copy into instrumentation and keeps the columns of figures honest, like a lab log book.

04

How it works

No image files and no libraries — everything is procedural Canvas 2D over a small density buffer. The three negatives (a 2 a.m. skyline, a portrait at a window, a groyne at low tide) are drawn from scratch at 288×384 with a seeded mulberry32 RNG, then downsampled to a 144×192 grid of target densities.

The signature element — the latent image

Exposing a sheet builds a per-pixel exposure array E from the negative, modified by contrast grade, exposure seconds, a little film grain, and — once in a while — a stray hair on the carrier that prints white. A requestAnimationFrame loop then integrates a per-pixel development time, and the visible density follows a first-order curve, D = E · (1 − e−devT/τ) — shadows first, highlights last, exactly like real paper. The crucial part: cursor movement over the tray calls a splat() that locally raises development, so the image comes up where you rock it. Past a fog threshold the developer starts reducing unexposed silver and the highlights veil over; stop halts it, a full fifty-five seconds in the fix makes it permanent, the white-light cord ruins anything unfixed, and a print pulled from the fix too soon slowly browns on the drying line. The verdict you get when you hang it is computed from the actual pixel densities, not a script.

  • Canvas 2D for the negatives, the developing print, the hung prints, and the drying line.
  • Inline SVG for the development-curve diagram — the same τ and fog maths the trays run on, plotted.
  • Pointer events for drag-between-trays and the agitation swirl; keyboard tongs as a no-mouse fallback.
  • DPR-safe, paused on hidden tabs, with a static readable fallback if a canvas context is refused.
05

Three passes

Pass 1 · Craft

The tray labels (small mono text) sat directly over the developer, which slosh-animates as you agitate — small text on a busy, moving region. Added a text-shadow scrim under every tray header so the labels hold in both the red room and under white light. Also widened the verdict line's reserved height from 1.4em to 3em: a two-line verdict was shoving the whole drying line down when it appeared.

Pass 2 · Depth

Made the room feel live rather than lit. The negatives now lift and brighten on hover, like fingering a film strip on the light box. The safelight bulb in the header breathes on a slow 5.5s cycle. And the hung prints sway on gently staggered timers, so the drying line drifts as though air moves through the basement — a detail you only catch on the second look.

Pass 3 · Hardening

Removed one ornament (the Chanel rule): a static wet-sheen gloss lay over the print and faintly veiled the very image you develop — cutting it sharpened the signature. Extended the reduced-motion guard to also kill the new bulb-breathe and print-sway. Re-checked the 375px layout (the three trays hold their row) and ran the full expose → develop → stop → fix → hang loop headless for console errors. Clean.