Snook.ca

Will Change

Dealing with CSS performance can be quite interesting. Various factors, like what properties we use, can have detrimental effects on the usability of a page.

I noticed that my site, as simple as it is, was having a bit of an issue with the fixed background that I’ve had. The scroll performance just wasn’t as smooth as I wanted it to be. I don’t remember this ever being an issue when I first built the site but browsers change.

I decided to switch the fixed background to a static background and all was right with the world. I doubt anybody even noticed the change.

Of course, not every performance issue is so easy and inconsequential to fix. Issues like box-shadow or scroll-jacking are often considered required on a project to achieve a designed experience.

With more CSS features comes more complexity comes more possibility of introducing performance issues. Browsers recognized that certain CSS features needed to be handled differently in order to be more performant.

For example, transforms—especially in 3D space—could be offloaded to a GPU to be rendered more quickly. It is for this reason that it was discovered that if we force an element into 3D space using translateZ, even if we’re not actually moving it anywhere, we could get the performance gains we desired.

By abusing a property in this way, an expectation is created between browser developers and web developers—and not a good expectation. It’s like using floats for layout. We’re using a feature in a way for which it was never intended.

Browser developers decided to introduce a new property that more directly dealt with the performance issues while also making expectations clearer for web developers.

CSS now has a will-change property that allows us to tell the browser what things are likely to change and thus provide a hint to the browser that it should optimize these things.

The will-change property is normally used by specifying what CSS property is likely to change on this particular element.

.modal {
will-change: transform;
}

In this case, the transform may or may not actually ever be applied to the modal but we’re trying to tell the browser that maybe it will.

In applying this property, however, there’s an interesting side effect.

If we were doing a transform:translateZ(0), the element would create a new stacking context and in some browsers, it’ll put the layer into its own rendering pipeline. Thus, giving us the performance increase we so desire.

Therefore, by applying will-change: transform, the browser will force the new stacking context regardless of whether the transform has been applied or not.

The key thing to note here is that will-change will only create a new stacking context if the property would also create a new stacking context. Since the transform property creates a new stacking context, will-change: transform creates a new stacking context.

If you did will-change: display then no new stacking context would be created because no display property value would create a new stacking context.

Let’s look at another example: opacity. An opacity with a value of 1 doesn’t create a new stacking context but a value less than 1 (like 0.9) does. When will-change is applied, then a new stacking context is always created.

Browser support

Not all browsers have will-change implemented yet. Therefore, due to the behaviour of it creating a new stacking context, you’ll need to specify a CSS property that does create a new stacking context to achieve similar rendering results.

Chances are, if you’re playing with layering elements on a page, you’re already using position and z-index to achieve the proper stacking order and therefore may not ever run into this rather interesting side effect of the will-change property.

@Thierry: Well, the same applies for using translate3d() or translateZ()—both also force browsers to render this in a different stacking context/on the GPU. I don’t see why this is a bad thing as if you’re going to use this property you have the intent to have it rendered on the GPU. Of course you need to test this but it is something a developer should do anyways when forcing an element to render differently.

In my research, and with Paul Irish's blessing - will-change is a no brainer on position fixed elements, but should be used sparsely on other elements. I wrote about it and linked to two great articles explaining the details.

@Peter: If it's a no-brainer on fixed positioned elements, I have to wonder why the browser doesn't automatically try to optimize this. I think the goes to Anselm's comment that Edge won't implement it because it's not needed. I suspect will-change is something that won't ever really gain much traction. Much like we no longer really need to optimize CSS selectors. (If we ever really did.)

@anselm: the translate3d hack is another good example of something people use as gospel, but is just a work around for WebKit/Blink constraints (not hardware accelerating 2d transforms). It never benefitted Opera (actually broke Opera for a time as it supported 2d but not 3d transforms) , and IE/Edge don't need it to put it on the GPU.

I agree that browsers should automatically optimize position: fixed. It's really unfortunate that Chrome doesn't do it, and that as a result will-change: transform is now mostly used to work around that particular shortcoming. I believe the reason they don't optimize it is that they lose subpixel text anti-aliasing if they create a layer for the fixed element, and they interpret will-change: transform as an indication that the author doesn't require subpixel AA in that particular element.
I work on Firefox and we've spent a great amount of effort to ensure that layerization does not affect text rendering. We layerize position: fixed and background-attachment: fixed by default. Moreover, we encourage people to only use will-change to hint at changes that the browser can't know ahead of time unless it reads the site's JS code and predicts what's going to happen.

Microsoft's position is surprising to me. will-change allows more optimizations than just hardware acceleration. Firefox on Windows uses hardware acceleration for all rendering, too, but it still benefits from will-change: transform if you're going to start a transform CSS animation on an element, because once the animation starts it doesn't have to create a new buffer for the animated element - it can just annotate the existing buffer for the element with the animation properties and have the animation run on the compositor thread.

Sorry, comments are closed for this post. If you have any further questions or
comments, feel free to send them to me directly.

Hi. My name is Jonathan Snook and this is my site. I write about what interests me, which is usually web design, development, and technology.
I wrote SMACSS. I tweet. Want to learn more?