Lindsay Edwards

Convenience wrappers assume the heavy case

On this page

I wanted a backdrop behind a 3D scene. Just an image sitting behind the geometry. The library had a helper component for exactly that, so I reached for it.

The canvas started losing its context. Again.

For anyone loading the page that meant the 3D scene flickering out or going blank, a broken centrepiece where a polished one was meant to be.

The helper was the culprit, and the reason was that it was doing far more work than I had asked for.

What the helper assumed#

To place a background, the helper routed the image through a loader for high dynamic range gainmap files, then built a pre-filtered radiance pass. That pass, often called PMREM, convolves the image into a set of blurred maps so a surface can reflect its surroundings realistically.

That is real work on the GPU. It is the kind of setup that can tip a context over the edge on its own.

Here is the part that stung: my scene’s material ignored environment lighting entirely. Nothing in the scene reflected anything. The expensive lighting pass was being built for a feature I was not using.

The convenient path assumed I wanted physically based reflections. I only wanted a picture behind the geometry.

Dropping to the primitive#

The fix was to stop using the helper and set the panorama directly as the scene’s background. The image is an equirectangular panorama, and I assigned it straight to the background with the blur set to zero.

Zero matters. Any non-zero blur triggers the same expensive pre-filtered pass I was trying to avoid, because a blurred environment is exactly what that pass produces. Zero blur means the image is used as-is, no convolution, no context pressure.

I also restored the previous background in cleanup, so the scene left things as it found them.

The second trap#

There was a second problem hiding behind the first.

Some of the scene used additive-blended sprites, the kind that brighten whatever is behind them. Over a transparent canvas, they did not brighten anything. They punched black rectangles straight through the page.

Additive blending needs something opaque underneath to add its light to. A transparent canvas has nothing there, so the maths gives you black.

The fix was to render the background opaque inside the GL pipeline itself, not as a DOM image sitting behind a transparent canvas. Once the background lived in the pipeline, the sprites had something real to composite against and the black rectangles were gone.

Drop to the primitive#

Convenience wrappers are built for the heavyweight case, because that is the case people struggle with. If you are using a cheaper material or a simpler format, the wrapper is often doing expensive setup you will never use, and sometimes that setup is what breaks you.

When that happens, drop to the primitive API and give it exactly what you need. Set the background directly, keep the blur at zero, restore it on cleanup.

And remember that additive blending only composites correctly over an opaque background inside the pipeline. The canvas alpha is not a backdrop. It is a hole.

Keep reading