Blog2 min read
A lake that never makes the page wait
How the WebGL lake at the top of this site paints in a worker, compiles its shaders alongside, holds to a pixel budget, and costs nothing when you can't see it.
The lake is the first thing you see on this site and the heaviest thing on the page. Painting its terrain takes the better part of a second on a laptop and several seconds on a phone. The water is a GPU simulation stepping every frame, and the creatures run on the CPU.
So it goes last. The rules: the text paints first, the lake never blocks input, and it costs nothing when you can't see it.

Painting off the main thread
The terrain paints in a Web Worker on an OffscreenCanvas. Layers come back as ImageBitmaps and data as transferred buffers, so nothing is copied on the way out. The painting code is loaded only when it's needed, and with a worker it never ships to the main thread at all.
Where OffscreenCanvas can't draw, painting waits for the main thread to go idle, with a timeout, since Safari has no requestIdleCallback. It still lands after the page has painted.
Never an empty box
- A 48 × 32 picture of the finished scene is inlined and blurred, so the hero reads as the lake coming into focus.
- The scene announces its first real frame, so the page fades it in instead of waiting on it.
- Creatures wait for the water, so nothing floats on an empty frame.
The GPU

Shader compiles overlap the painting. Linking starts early; the blocking "did it work?" question is only asked at the first frame, by which point the programs have compiled.
The simulation steps at a fixed 60 Hz, whatever the display runs at, on half-float targets. If WebGL2 or float render targets are missing, it falls back to a still painting with the creatures still moving on top.
Pixel budgets. The device pixel ratio is capped at 2 and the width at 1,700 pixels: enough for a sharp scene without painting a poster.
components/hero/lake/scene.ts
function measure() { const rect = fxCanvas.getBoundingClientRect(); if (!rect.width || !rect.height) return null; const dpr = Math.min(window.devicePixelRatio || 1, maxDpr); let h = Math.round(rect.height * dpr); if (h * W > maxWidth) h = Math.floor(maxWidth / W); return h;}Resizing is cheap. Small changes, a scrollbar or a zoom step, don't trigger a repaint. A real resize shows the nearest existing painting while the new one is made.
Costs nothing when you can't see it
- It pauses off screen (an
IntersectionObserver) and in hidden tabs. - Under reduced motion it draws one still frame.
- It recovers from a lost WebGL context.
Sound cues moved off the input path too, so a splash never waits on the audio context.
How it was measured
- On an isolated production build, never the dev server.
- In headless Chrome on the real GPU.
- With the Long Animation Frames API to find main-thread stalls, because a full trace changed timing enough to hide them.
- Variants compared back to back, because GPU numbers swing with machine load.
The temporary instrumentation was removed once the work was done.
Keep reading
A load balancer from scratch, A/B tested on live trafficA load balancer in Node for five moderation replicas: four strategies, a circuit breaker and health check each, failover, and a load test that proves the spread.Read
Evals that keep an AI agent honestHow the AI agent on this site is tested like code: 44 questions against the real model, retrieval gated in CI, a versioned prompt and a trace under every answer.Read
Shipping to one box without a staging serverCI/CD for a live EC2 box with 3,000+ members and no staging server: an SSH push with pinned keys, one deploy at a time, a 90 second health gate and a rollback.Read
Voice rooms on an SFUHow IABTM's live audio rooms moved from a peer-to-peer mesh to a mediasoup SFU: a router per room, five-speaker stages and a worker that restarts on its own.Read
A chapter site that keeps itself current from InstagramA FastAPI service that reads an IEEE student chapter's Instagram, has Gemini turn each post into a structured event, drops reposts and feeds the chapter's PWA.Read