Particles rasterizing particles
A scroll effect that dissolves the page into sand, layered onto a hero that is already an animated particle field. The measurements were worse than the guess, and the fix was to want less.
The QED mark is a live particle field. It morphs between formations on a canvas, every frame, while you look at it. Then we added a scroll effect that dissolves the whole page into drifting grains that reassemble as you scroll.
Both are lovely. Together they were a disaster, and the reason is funny once you see it.
The measurement
The dissolve works by capturing its entire subtree as a texture every time that subtree repaints. A page of static text repaints rarely, so the capture settles and the cost goes to nothing.
An always-animating canvas repaints sixty times a second. So the capture never settles. It re-captures, re-uploads, and re-builds mipmaps forever, of a picture whose only moving part is a particle field. Particles rasterizing particles.
Idle CPU, with nobody touching the page:
- particle field alone: 19.5%
- particle field inside the dissolve: 70.7%
What we tried before giving up on it
We did not surrender immediately. The field was moved out of the captured subtree entirely, rendered through a portal so the effect could never see it. That worked: 70.7% fell to 38.3%, and to 9.0% once scrolled past the hero.
Then the layout grew side rails, and a second problem arrived. A sticky element has a painted position and a document position, and those disagree. The dissolve scatters by document row, so the rails flickered into dust while standing still. We taught the shader to hold a band solid.
Then the reading page needed two scrollers, one for the page and one for the effect, and scrolling up from the footer meant rewinding an entire post before the window moved at all.
The actual fix
Each patch worked. The pattern was the problem: an effect that only fits if everything around it bends. So we stopped bending things.
The dissolve now runs on landing surfaces only, where a visitor meets it once on arrival. Content pages scroll plainly. The band logic came out, the rails came back, and the shader is closer to stock than it was three days ago.
What survived
The useful things were all side effects of the investigation. The particle field now pauses when it scrolls out of view, which took idle cost to 0% below the fold for every visitor on every browser. Heavy components load as their own chunks instead of shipping to people whose browsers cannot use them. And the scroll-linked pieces learned to find their own scroll container instead of assuming the window.
One more thing, and it is the part worth remembering. For two days the effect
looked broken in testing: content stuck as dust, never reassembling. It was the
test harness. Setting scrollTop directly does not drive the component the way
a wheel does. Once we drove it with real wheel events it had been working the
whole time.