July 01, 2020
Recently I have been wondering how CSS features like painting backgrounds compare to CSS Houdini’s Paint API in terms of performance. CSS on it's own is already pretty powerful and can paint lots of amazing things (e.g. this collection of single div paintings). To figure out the difference in performance I created two pens, that essentially do the same thing: paint 200 divs, with 9 colored background circles each.
For the setup I created two pens: One time in Houdini and one time in pure CSS using radial-gradient backgrounds. They essentially do and look the same.
CSS-Only Code
.circles {
background:
radial-gradient(circle, #01FFFF 60%, rgba(0,0,0,0) 60%),
radial-gradient(circle, #01D5FF 60%, rgba(0,0,0,0) 60%),
…;
}
Houdini Code
.circles {
--circles-count: 3;
--circles-offset: 1;
--circles-opacity: 100%;
background: paint(circles);
}
To test the rendering I decided to run both examples in Codepen debug mode through WebPageTest using a Moto G4 on Chrome.
What I found was quite interesting. If we compare the summary of both pens, the First Contentful Paint happens a bit faster in CSS than in Houdini, the difference is 0,051s. The DOM was also interactive more quickly in CSS: in 0,941s vs. 1.972s in the Houdini examples. That’s more than a 1 second difference! This is most likely due to the extra loading that needs to be done for the Houdini module.
Another notable difference was the loading size: all the CSS features are already built into your browser, so in the CSS example we have 4KB to load, in the Houdini example we also have to load the JS for the painting feature, which amounts to 9KB total.
Looking at the Processing Breakdown gave some more insights. Layout for both examples is quite large between ~60 - 90ms, which makes sense, since they both have 200 divs to layout. The notable difference is in the painting and scripting part. While the CSS example has no scripting, the painting takes around 43ms.
In the Houdini example the painting takes a lot less around 20ms, but it has the additional scripting part of 23ms. So they both actually amount to the same time: 43ms.
Since I wanted to retest the examples on desktop, I chose to run a second test on Edge Chromium.
On Edge the First Contentful Paint was also 0,035s faster in the pure CSS example. And the DOM was interactive within 0.6s vs. 1.03s in the Houdini example, which makes a difference of 0,43s in DOM Interactivity. This is similar to the previous outcomes, only with less difference between the two examples, since the desktop is faster in loading and processing.
In terms of processing the CSS only version had a painting time of 18ms. The Houdini version had a painting time of 9ms + a scripting time of 7ms, which amounts to 16ms painting and scripting. So they amount to roughly the same, with the difference that the Houdini example needs to do the extra scripting work to paint everything.
These outcomes are also very similar to the outcomes on the Moto G4. The desktop is just quicker in doing all the layout, painting and scripting work.
CSS Houdini is great to progressively enhance your site with interesting features if the browser support is given. Otherwise you can fall back on a supported CSS features. You can test this easility with the @supports(..)
rule in CSS or the if (CSS.paintWorklet)
rule in JS.
CSS test for support:
.circles {
background: radial-gradient(circle, #01FFFF 60%, rgba(0,0,0,0) 60%);
}
@supports(background:paint(circles)) {
.circles {
background: paint(circles);
}
}
JS test for support:
if (CSS.paintWorklet) {
/* ... */
}
If you want to learn more about Houdini, I can recommend this collection of awesome Houdini resources and checking this website to see the implementation status of the different Houdini APIs in the different browsers. The Paint API already has pretty wide support in most browsers and Firefox is intending to implement it as well.
CSS Houdini is really powerful and the Paint API is making CSS painting a lot more flexible. A few months back I implemented generative background example and that would be really hard to do in CSS only.
Looking back at the performance examples I tested in this article, I can conclude the following: Loading CSS Houdini features definitely adds a bit of an overhead, depending on the complexity and size of the Houdini module. If your site should be painted and interactive super quickly, you should consider a CSS fallback until the Houdini module is loaded and ready. It can be the nice feature on top, that makes your site a bit more extra.
Overall, in terms of painting performance, there really wasn’t much difference between the CSS and the Houdini example. Houdini needed more time scripting, but less time painting. CSS on the other hand didn’t need any scripting, but more time painting. It really depends how complex your Houdini script is, but that also true for CSS if you use a lot of CSS logic that takes the browser long to parse (like complicated sibling or child selectors). If the Houdini module script is really complex with lots of calculations, this could probably lead to performance losses.
Overall the Paint API is especially great for painting, that otherwise wouldn’t be possible in CSS or would need lots of extra DOM elements, that aren’t really necessary. You can defintely progressively enhance any page with interesting Houdini modules, because they're easy to load and you can adapt them with Custom Properties.