I think I finally understand CSS will-change and how to scale DOM content effectively. Maybe. Let me lay out what I know in case it's helpful or there are gaps in my knowledge:
The browser groups elements into layers to be rasterized separately and then combines these layers into the final rasterized image. It's got its own set of rules for which elements can be grouped into a layer and rasterized together. A common way layers are created is transforms.
In order to rasterize a complex scene effectively, you want to separate content that changes together visually into a common layer. In my case, I'm helping build @WithHQ which has a zoomable viewport. At a minimum, we want to separate the static UI from the dynamic scale content.
To get a clearer picture of which elements are being rasterized together, you can use devtools. Namely, the "layer borders" option under Rendering tools, and also by enabling advanced paint instrumentation and opening the Layers view of a recorded frame.
When we started seeing rendering slowdown on With, I noticed it was because accessories in the room were being rasterized separately. So I tweaked our rendering characteristics by using will-change to 'hint' to the browser that all accessories should be rasterized in one layer.
Since I didn't fully understand will-change, I made this a static CSS property of the accessory container. This fixed the framerate, but had a huge drawback: zooming in resulted in blurry text and images. Everything was getting rasterized at scale=1, then scaled up with the zoom.
Finally, today, I had the breakthrough. Why is will-change named that way? It's not just a static declaration to the browser that "these things sometimes animate together." It's a realtime hint about animations that are happening right now.
Taking a step back, what did I actually want? When the viewport is animating (pan, zoom), I want everything rasterized as one big image, just once - then just transform that texture. Blurry scaling is fine because the whole scene is already in motion, it won't be noticed.
But when the viewport has settled, I want to re-rasterize the whole scene, so that the individual accessories will be properly scaled to the current zoom level and text and images will be as crisp as if they were natively that size.
So the solution is to animate the will-change value along with the transform (like the docs say to... I'm just a bit dense). Changing will-change starts and stops re-rasterization, under my control.
When the user is panning or zooming, will-change = transform. This basically tells the browser to reuse the rasterized context and minimize changes while the property is set. I'm sure there's a more technical explanation, but that's the gist as I understand it.
When the animation has stopped, I set will-change = initial. This hints to the browser that the motion is done, it's time to refresh our understanding of the rasterization context and repaint the new world.
Now moving the view is fast, but when the view settles the contents are re-rasterized for crisp, sharp textures.

Framerate is still not perfect, but it scales much better as content is added (1 layer vs n layers), and it looks perfectly clean!
will-change is a simple property, but combined with an understanding of layers it seems to hold a lot of optimization power. For instance, perhaps we could separate the wallpaper into a separate layer and keep will-change on permanently for it -
That would mean that the wallpaper scales with a bit of fuzziness at high zoom, but that may be a good tradeoff to reduce rendering overhead. We can optimize different raster layers in different ways, contextual to the needs of the application.
By the way, With just shipped some big changes for the new year and we're getting close to opening the floodgates! If it looks like a tool your team could use, get on the list at https://with.so !
You can follow @gaforres.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled:

By continuing to use the site, you are consenting to the use of cookies as explained in our Cookie Policy to improve your experience.