Build guide
How this archive was built
The brief
A portfolio for a photographer who shoots what cameras cannot hold: motion, darkness, glare. Presented as a dense, mono labeled archive index. Every photograph is procedurally generated in the page, then passed through house fail mode treatments implemented as real WebGL image processing. Clicking a print opens a darkroom viewer where the treatment develops in real time.
- Register chamber
- Fail mode images. The camera failing beautifully: long exposure, cyanotype, photocopy grain, drift.
- Palette
- Prussian #0e2438, cyanotype blue #1e4d7b, paper #f4f1e8, and flare white as the single stroke. Light itself is the accent.
- Type
- Fragment Mono for labels, captions and data. Figtree for display. Two faces, no more.
- Constraints
- Zero dependencies beyond Google Fonts. Raw WebGL, vanilla JS, no build step. Sharp corners, tone tiers, one stroke color.
The signature technique
The whole visual identity is one WebGL fragment shader: the treatment rig. Every thumbnail and every darkroom view runs through it. It takes a raw procedural exposure as a texture and develops it into one of four processes, controlled by a develop uniform that sweeps from undeveloped paper to finished print in blotches, the way liquid finds paper. The rig is its own module with no knowledge of the generators, so it can process any image source.
/* development sweeps in blotches, like liquid finding the paper */
float blotch = fbm(uv * 3.5 + u_seed * 5.7);
float dev = smoothstep(0.0, 1.0, u_develop * 1.45 - blotch * 0.45);
vec3 col = mix(undev, treated, dev);
/* film grain, heavier while developing */
float grain = hash(uv * u_res + fract(u_time) * 17.0) - 0.5;
col += grain * mix(0.085, 0.045, dev);
The photographs themselves are honest about their numbers. Each print's EXIF caption is generated from the parameters that actually drove its drawing: the shutter time sets how many steps the light walk takes, the aperture sets how many blades the flare grows, the iso sets how many hot pixels the sensor invents. Same seed, same print, every time.
The critique passes
-
Pass 1
Captions, dead zones, weak light
Found: grid captions wrapped into a justified jumble, series rows left three-cell holes, the flare series repeated one centered starburst, the hero light walk read as dim scattered orbs, and the flare white stroke was indistinguishable from paper. Changed: two-line designed captions, spans that sum to full rows in numeric order, sun scale and haze variance, doubled trail weight, halation on the stroke. Text never sits on artwork, so scrims were verified structurally clean. Upgrade: develop on scroll. Prints sit undeveloped in the bath until they enter the viewport, then come up staggered.
-
Pass 2
Treatments drowning their subjects
Found: halftone turned dark exposures into a uniform dot field with no image, cyanotype sank its midtones into empty frames, chromatic drift ran dim. Changed: a hard contrast remap ahead of the dot screen, a lifted cyanotype ramp, a small drift gain. Upgrade: contact sheet mode. Because the span math fills rows exactly, every series collapses to one eight-frame strip, a true contact sheet, linkable and keyboard reachable.
-
Pass 3
One hot glow, one repeated pose
Found: the stroke halation was far too hot, reading as a rendering error, and every slow body drifted the same direction at the same scale. Changed: the glow cut to a whisper, walk direction and figure scale now vary per seed. Upgrade: lift the negative. Resting the pointer on a developed print shows the raw untreated exposure underneath it.