Lacuna

Light the Thing, Don't Colour It

DisciplineReal-time graphics · interaction design · sound · accessibility
Period28 August – 7 September 2026
RoleSole author, working alongside a second, independent agent on the same files.
StatusHome page complete. Visual direction in revision.
01

At a Glance

12
full-height keys, C→B — a chromatic octave that was already the page's column grid
4
GPU passes running a two-family damped wave equation with real collision
20
distinct bugs found, fixed and indexed with root cause
16.2 : 1
measured contrast ratio, against a 4.5:1 accessibility floor
62px / 54.8px
measured mobile tap targets, against a 48px target
3
creative pivots carried on one unchanged physics engine
0
photons the wave simulation contributed to anything visible — until Phase 12

The brief

A personal portfolio hero. What it became is a playable instrument: a page-wide fluid field with twelve keys cut into it, where striking a key at a different height is a physically different event, not just a visually different one.

Included here honestly. The engineering is finished and verified. The design is not settled, and the project's own author has said so.

02

The Physics

A four-pass GPU pipeline implementing a genuine two-family damped wave equation with real collision detection between distinct wave bodies — colour identity (indigo mass against wine mass) falling out of the physics itself rather than being painted on afterwards. A sum-union rather than max-union aperture construction avoids the phantom creases a naive max-blend leaves at internal seams.

Immortal mounds. Low-frequency residual waves have a near-zero Laplacian, meaning velocity-based damping literally cannot see them; only height decay ever kills them. Fixed, then protected with an explicit TUNING CONTRACT comment specifically so a second, independent agent working the same files would not quietly re-break the fix.

Transfer saturation, or "white balloons." Broad swells were pushing the colour ramp to its ceiling across whole regions, bleaching them uniformly and erasing the fine detail riding on top. Solved with genuine scale separation: a wide local mean drives broad body tone, the residual drives fine crest and ridge detail. A real band-split, correctly applied to a colour problem.

Wave packets. Emitter injections produced only broad, undifferentiated swells with no legible fronts. Fixed with a cosine carrier under a Gaussian envelope, producing genuine concentric fronts at a fixed absolute wavelength, so ripple fineness survives regardless of emitter size.

03

Three Pivots, One Engine

Phase 1 — the vessel. The simulation contained inside the portfolio's existing stepped-aperture shape, with real basin walls to rebound from.

Phase 2 — the elevator bank. A page-wide simulation with twelve elevator-shaped windows into it, activation entirely cursor-energy-driven and decaying to true invisibility at rest.

Phase 3 — the piano. The decisive pivot: recognising that the twelve-column layout was already a chromatic octave, and remapping click depth as a genuine musical register — simultaneously driving density, sustain, colour palette and the physics injection itself, so a bass strike and a treble strike are physically, not merely visually, different events.

The register-palette discipline — the sharpest creative-technical judgment in the build. A strike whips the local field into bright crests. Any colour mapping that simply inherited the field's brightness would wash every key toward the same ghost-grey regardless of intended register. Fixed by making each register's colours a fixed family, with the physics allowed to modulate luminance only within that family, never escaping it. A real decision about where the simulation has authority and where the design language has to hold the line.

Cross-modal integration. Press height drives pitch, timbre and visual register together, with decay times matched one-to-one between what is heard and what is seen. Synaesthetic by design, not sound bolted onto a finished visual.

The white-ground decision. Pushing the baseline state as close to nothing as possible — near-white ground #fafaf8, black type, all ornament removed — makes the coloured physics response read as a real transformation rather than competing with an already-busy page. Minimalism chosen because it serves the thesis, not despite it.

04

Phase 12: The Depth Pass

A four-lens audit converged on one finding with hard pixel evidence: the object was drawn, not lit. Each ivory key sat at one unvaried hex for 620 consecutive pixels. A declared light source shaded nothing. And — the most striking part — the wave simulation had been running invisibly the entire time, contributing zero photons to anything visible.

The idle-breath fix — luminance-only modulation from the field, zero chroma — finally connected months of prior fluid-physics work to the surface, activating work that had run in the background the whole project without ever mattering visually.

The principle from the very first design log, resurfacing unchanged: light the thing, don't colour it.

05

Geometry as a Contract

In Phase 10 the keyboard was rebuilt as a single lit object, and geometry became a hard contract in keyGeometry.ts — asserted at module load, throwing if measurements do not sum correctly, so the geometry cannot silently drift.

Two deliberate spec deviations, both reported rather than silently made:

  • A multiply-blend reflection that would have been mathematically invisible on near-black lacquer, replaced with an additive approach inside the same cap.
  • A hard reflection cap that measurably produced 3.5 seconds of flat, static-looking time on loud strikes, replaced with a soft knee that holds the same ceiling while letting the reflection breathe from the first frame.

The colour pipeline was measured, not assumed. An eleven-step grey calibration strip rendered through the actual live pipeline determined whether the drawing buffer sRGB-encodes its output — finding an earlier assumption measurably wrong and removing it rather than leaving mismatched maths in place.

Design reuse, semantic rather than decorative. The lighting model built for an entirely separate client project ported into the cursor — but adapted so the orb's ink carries the same register-family logic as the piano, making the cursor an extension of the instrument's logic rather than a decoration riding alongside it.

06

Working Beside Another Agent

Discovered mid-build that a second, entirely separate agent was independently editing the same files with zero prior coordination. Protocols developed on the fly and kept since:

  • COORDINATION NOTE file headers as a contract channel.
  • TUNING CONTRACT comments protecting hard-won constants.
  • Compare-and-swap edit patterns to survive concurrent writes.
  • A standing merge over replace discipline — the other agent's genuinely good contributions (two-family state packing, the pressure-ring mechanic, full-height key plates) were adopted rather than fought or discarded.

The risk materialised for real: a co-agent removed a uniform while a write to it remained elsewhere. Caught and resolved through the coordination-note protocol built for exactly that case.

07

Verification

  • Hex-exact colour sampling against declared material values.
  • Measured reflection decay times.
  • Contrast ratios of 16.2:1 and 13.8:1 against a 4.5:1 floor.
  • Mobile tap targets measured at 62px and 54.8px against a 48px target.
  • A cheap and clever squint test — an 8px Gaussian blur applied to a screenshot to check whether the composition reads at a glance, independent of fine detail.
  • Verified at three resolutions, reduced-motion support, a dev-only QA capture pipeline with deterministic seeded fast-forward, raw diagnostic field views and headless still and video capture.

The most expensive bug was SwiftShader silently stalling the renderer with zero console errors — costly precisely because it produced no error signal at all. Also caught: a React StrictMode remount recovering a WebGL context that cleanup had explicitly killed.

08

Standing Rules the Project Produced

  • Light the thing, don't colour it. A colour problem is usually a lighting problem.
  • Make geometry a contract that throws, not a set of numbers that drift.
  • Let the physics modulate luminance inside a fixed family; never let it choose the family.
  • Report spec deviations; do not make them silently.
  • Measure the pipeline before trusting an assumption about it.
  • Protect hard-won constants in the file itself, where the next editor — human or not — will see them.
  • Merge over replace when another hand is in the same file.
  • Blur the screenshot. If the composition fails at 8px of blur, detail will not save it.
09

What It Demonstrates, and What It Doesn't

Twenty indexed bugs, a real fluid simulation driving a real instrument, and an accessibility record most commercial sites do not meet. It is also the project whose author looked at the finished result and said the design does not yet earn attention — which is why it sits in the work selection rather than organising the portfolio around itself.

Included with that judgment intact. A portfolio that only shows work its author is uncomplicated about is not a record of a practice.

Assets to attach: the instrument at three resolutions, the diagnostic field views, the calibration strip, the squint test.

Next record · 07The Portal Pattern