The Litmus
Inspired by Gasochromic on Wikipedia
Built with Pure WebGL2 · RGBA16F FBO ping-pong · Separable Gaussian bloom · Speed-to-color sigmoid mapping
Techniques SDF capsule deposits · 6-substep trail rendering · Cross-diffusion spreading · Additive blending · Simplex grain · Reinhard tone mapping
Direction Move to react. Fast for acid, slow for base.
Result A dark surface that remembers every movement as a chemical stain — scarlet where you rushed, deep blue where you lingered, purple at the boundaries
The Story
Gasochromic materials are chemical witnesses. Thin films of tungsten trioxide sit transparent and inert until hydrogen gas touches them — then they turn blue. Remove the gas, they clear again. The material doesn’t think or decide. It just reacts. Its color is a direct, reversible record of what it was exposed to.
The broader class of chromic materials includes thermochromic (heat-sensitive), photochromic (light-sensitive), and electrochromic (voltage-sensitive) systems. What they share is that the material’s state becomes visible. You don’t need an instrument to read the measurement — the measurement IS the color. The surface is the sensor.
Litmus paper is the most familiar version. Acid turns it red. Base turns it blue. The paper doesn’t analyze the solution — it embodies the answer. The color IS the chemistry.
This experience makes your cursor the reagent.
The Take
A dark surface. Almost black, with just enough depth to feel like a material rather than a void. The prompt says “move to react.”
Move your cursor fast and the surface burns red — vivid, hot crimson that blooms outward from your path. The faster you move, the more saturated and intense the red deposit becomes. Slash across the screen and a scarlet streak remains, glowing with its own internal light.
Now slow down. Move deliberately, carefully. The deposit shifts — from red through purple into deep electric blue. The slower you move, the deeper the blue. Stop entirely and the blue pools outward, a still stain of alkalinity.
The surface remembers. Each deposit persists, fading slowly but remaining visible for seconds. Move fast in a circle, then slow through the center, and you’ve painted a red ring around a blue core — acid surrounding base, with purple at the boundary where they meet. The diffusion spreading means the edges of each deposit bleed gently into their neighbors, creating soft gradients rather than hard boundaries.
The text develops through cumulative exposure. Rush around enough and “acid” appears. Linger long enough and “base” follows. Keep painting and eventually: “every surface is a sensor.” Then, finally: “the solution was you.”
It’s a double meaning. The solution — the answer — was always your own movement. But also: you ARE the solution. The chemical mixture. Your velocity determined the pH of every reaction on this surface.
The Tech
Pure WebGL2 — FBO Ping-Pong
No Three.js. Four shader programs and two RGBA16F framebuffers alternating roles. The CPU computes cursor speed and maps it to an acidity value; the GPU handles deposit rendering, diffusion, bloom, and compositing.
Speed-to-Color Sigmoid Mapping
Cursor speed (in CSS px/s) maps to an acidity value between 0 (base/blue) and 1 (acid/red) via a sigmoid function:
acidity = 1 / (1 + exp(-(speed - 250) / 80))
At 0 px/s: pure blue. At 250 px/s: purple (the neutral point). At 500+ px/s: pure red. The sigmoid ensures smooth transitions rather than hard thresholds — there’s always a gradient between acid and base.
SDF Capsule Deposits
Each frame, the cursor movement is divided into 6 sub-steps. Each sub-step renders a capsule-shaped deposit using a signed distance function:
float sdSegment(vec2 p, vec2 a, vec2 b) {
vec2 pa = p - a, ba = b - a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba * h);
}
The deposit has a tight Gaussian core and a wider glow halo, both colored by the acidity value. Sub-stepping prevents gaps in the trail when the cursor moves quickly.
Cross-Diffusion Spreading
Each frame, the trail buffer is processed through a 5-tap cross diffusion kernel before new deposits are added. This causes each stain to spread slightly outward — simulating the way a drop of dye spreads through wet paper. The fade factor (0.997 per frame) gives a half-life of about 3.8 seconds at 60fps.
Dual-Pass Bloom
The trail buffer is downsampled to half resolution and blurred with two separable Gaussian passes at different radii (2px and 4px), creating the characteristic soft glow around each deposit. The bloom is composited at 1.4× intensity, adding the luminous quality that makes the stains feel like they’re producing their own light.
Mobile Idle Demo
On touch devices, after 1.5 seconds of inactivity, a virtual cursor traces a figure-8 (lemniscate of Bernoulli) with varying speed. The speed variation naturally produces alternating acid and base deposits, building up a red-and-blue pattern automatically. The varying speed of the lemniscate means the figure-8 itself becomes a gradient — fast at the crossover point (red), slow at the extremes (blue).
Source: Gasochromic on Wikipedia