How SIGNAL was made
This site is one of five built by Claude Fable 5 (Anthropic's Claude, running in Claude Code) as a demonstration of AI-driven web design — no templates, no frameworks, no design tools. One HTML file, hand-written CSS, and a custom WebGL shader. Here's the recipe, so you can do the same.
The concept
The brand's core idea is "digital is the noise; we are the signal." So the site literally performs the idea: a full-screen WebGL fragment shader renders a field of TV static that resolves into a clean oscilloscope waveform as you scroll. The page loads in a burst of static (noise) and tunes itself into clarity (signal). Every design choice hangs off that one metaphor — the phosphor-green palette of an oscilloscope, the monospace HUD readout, the film grain, the "tune in" scroll cue.
The stack — deliberately primitive
- One
index.html. No build step, no framework, no dependencies. - Raw WebGL 1.0 with a hand-written GLSL fragment shader — ~90 lines.
- Fonts: Instrument Serif (display, italic for emphasis) + IBM Plex Mono (labels/body) from Google Fonts.
- Vanilla JS: IntersectionObserver reveals, a custom cursor, a word-splitting animation, and five shader uniforms.
The shader, in plain words
The background is a single full-screen triangle. The fragment shader mixes four layers: hash-based static (per-pixel white noise, re-seeded every frame), interference bands (a high-frequency sine over Y, raised to a power so only peaks survive), fbm fog (fractal Brownian motion — four octaves of value noise), and the waveform — a horizontal line whose Y position is a sum of sines plus fbm wobble, drawn with exp(-distance) falloff for the phosphor glow.
float d = abs(uv.y - waveY);
float core = exp(-d * res.y * 1.6); // sharp line
float glow = exp(-d * 34.0) * 0.055; // soft halo
Three uniforms drive the story: uScroll (page progress moves the line), uClarity (0 = pure static, 1 = clean signal — eased toward scroll progress each frame), and uBoost (hovering a service row excites the wave's amplitude and frequency). The intro burst is a trick: clarity starts at −0.7, so the "noise amount," computed as (1 − clarity), briefly exceeds its normal maximum, then eases to calm.
Typography & motion rules
- Two typefaces only; the serif never appears small, the mono never appears large.
- Italic + phosphor green is reserved for the emotional word of each sentence — signal, heard, edit bay.
- Headlines split into words, each wrapped in an overflow-hidden span, translated up with a 38 ms stagger and an expo-out curve.
- Everything animates once, on first intersection — no looping distractions except the ticker and the shader itself.
Craft details worth stealing
- Film grain: an inline SVG
feTurbulencedata-URI, animated with a 4-stepsteps()transform loop — costs no bandwidth. - Custom cursor with
mix-blend-mode: difference, plus a label that names each link's destination ("the making-of", "say hello"). - The HUD (bottom-left) reads the real shader state — the percentage is the actual
uClarityvalue, not fake. prefers-reduced-motionfreezes the shader time and disables all reveals.- DPR is capped at 1.75 so the full-screen shader stays cheap on 4K screens.
Deploying to Cloudflare
The whole site is a static folder, deployed with Wrangler in one command:
npx wrangler pages project create tdc-signal
npx wrangler pages deploy ./signal --project-name=tdc-signal
No config, no CI. Cloudflare Pages serves index.html at the root and this page at /guide/ automatically, with free TLS and a global CDN.
The process
Built autonomously in Claude Code: write the full site in one pass → serve locally → screenshot at five scroll positions plus mobile with headless Chromium → critique the screenshots like a design director → edit → repeat. Three full iteration passes; the waveform was slimmed (it rendered as a "laser worm" in pass one), the ticker contrast raised, the HUD and hover-reactive shader added in pass two.