ENGINEERING
The bloom pass was eating the alpha channel
2026-07-30 · 5 min read
The marketing site has a full-screen WebGL scene behind the page, and the page has a CSS dot grid behind that. The canvas is transparent so the dots show through. That was the intent for weeks before it was the behaviour.
The symptom was that the dot grid vanished. Not partially, not only behind the model — everywhere. The canvas painted an opaque sheet over the whole viewport despite being created with an alpha buffer and cleared with zero alpha.
Everything that was not the cause
The renderer was correct: alpha enabled, clear colour set to fully transparent black. The body background was correct. The scene had a backdrop plane, which turned out to be a separate and real bug — it faded too slowly and read as a dark disc at some scroll positions — but fixing it did not bring the dots back.
It was the post-processing
The scene runs through an EffectComposer with a bloom pass. Bloom is implemented as a chain of fullscreen quads, and its internal blur and composite shaders hardcode the output alpha to 1.0. Its final additive blend therefore drives alpha to roughly one across the entire canvas — not only where the model is, because the blur spreads everywhere.
No renderer setting fixes this, because nothing is misconfigured. The pass is doing what it says; it simply was not written with a transparent canvas in mind.
Stash the alpha, put it back afterwards
The fix is two small custom passes around the bloom. The first copies the pristine render — which still has correct alpha — into a spare render target. The second, after bloom has run, samples bloom’s RGB and the stashed target’s alpha and recombines them.
Three lines of shader and about forty lines of pass plumbing. The glow is unchanged and the page behind it is visible again.
Why this is worth writing down
Because the bug survived several attempts, and each attempt looked reasonable. When a transparent canvas is not transparent, the instinct is to check the transparency settings, and all of them were right. The pass that broke it was three layers away from anything named alpha.
If every setting that controls a behaviour is correct and the behaviour is still wrong, something downstream is overwriting the result rather than reading it.