Writing

Bloom, measured at zero

2 min

Built properly before I doubted it

I built the bloom pass properly before I doubted it: an offscreen framebuffer, a bright-pass, a separable blur, an additive composite back over the scene. The standard recipe, written against raw WebGL2 — and I was pleased with it. Then I put the frame next to the same frame without it, and preferred the one without.

What the frame said

Bloom is only affordable at quarter resolution, and at quarter resolution a 2px point becomes sub-pixel — so the downsampled bright-pass had nothing left to find. Rendering the frame at glow strength 0.85 and at glow strength 0 produced pixel-identical output. Worse, the offscreen round-trip was costing real light: the peak channel value fell from 255 to 242 against drawing straight to the canvas.

It was spending a framebuffer and three passes to produce a slightly dimmer image.

I might have talked myself into keeping it anyway — bloom feels like quality, and I had already written it. The throttled-device run settled it: the harness that exercises the degradation ladder reported bloom contributing zero glow while costing light, the top entry on the list of things it caught that nothing else did. A post pass is a hypothesis about where light is being lost. This one was wrong.

The glow moved into the point

A point sprite already carries the thing bloom was trying to recover — its own radial coordinate. Two overlapping gaussians in the fragment shader, a tight core and a wide dim halo, give emissive bleed at exactly the scale it is needed, for no extra passes at all. Warm points get slightly more of it, because a readout should look lit rather than printed.

The pass came out, the glow stayed, and the renderer got smaller. What I keep from this is the order of operations: build the thing, measure the frame, let the measurement win.

All writing