← Atelier Umbra

How this site was made

A guide to the stack, the techniques, and the deployment — so you can build your own dark room.

Atelier Umbra is a single HTML file. No framework, no build step, no bundler. Everything — markup, styles, shader, cursor — travels together, which is fitting for a studio that only makes one print.

The key light

The hero is a full-screen WebGL fragment shader drawn on one triangle. The wall is five octaves of value-noise FBM composited at three scales (plaster, texture, tooth). The light that follows your cursor is a Gaussian falloff exp(-d²·3.2) tinted warm (≈2800K), with drifting dust motes hashed per grid cell and only visible inside the beam. Cursor position is eased with p += (target−p)·0.055 so the lamp has weight.

The headline is lit by the same lamp: a duplicate layer of the title is masked with a CSS radial-gradient whose center is driven by the same eased coordinates via custom properties (--lx, --ly). One light source, two rendering systems.

The light-meter cursor

On fine pointers, the native cursor is replaced by a dot and a trailing ring that reads like a light meter: each section carries a data-f aperture value, and the ring's label updates as you cross into it — wide open at the dark hero (f/1.4), stopped down by the footer (f/11). On touch devices the cursor vanishes and the key light drifts on a slow Lissajous path instead.

Typography & palette

Image slots

Every portrait is a real <img> with a stable, descriptive filename (assets/noir-portrait-01.jpg …). The files currently hold rendered gradient stand-ins — layered radial glows, vignette and grain — so the design reads correctly today and the real photography can be dropped in later without touching a line of code. Portraits rest at brightness(0.52) and are lit to full only under your hover, like prints under a viewing lamp.

Access & restraint

prefers-reduced-motion stills the reveals, the scroll cue, and slows the light to a candle's pace. The shader pauses entirely when the hero leaves the viewport. The whole site ships under 40 KB of code plus seven placeholder frames.

Deployment

Deployed to Cloudflare Pages as a static folder — no build configuration at all:

npx wrangler pages deploy set1-a --project-name=set1-a

Wrangler creates the project on first deploy and returns the live URL. Three self-critique passes (desktop and mobile screenshots at five scroll depths) were run before shipping; the changes are logged in the repository's NOTES.md.

Designed and built end-to-end by Claude Fable 5.