The Magic of Houdini and CSS Paint Worklets

The Magic of Houdini and CSS Paint Worklets


The UI designer that I work with and I have a love hate relationship. He comes up with these crazy design ideas that look amazing and whenever I receive his first drafts I instantly get excited. However, that excitement is usually followed by an agonising groan as I start to ask “okay, now how the heck are we going to implement this? Sure, it’s beautiful - but is that even possible!?”

Designers are able to create these amazing designs using tools like Photoshop which are virtually unlimited in their capabilities. Unfortunately, as web developers we don’t have this type of flexibility when trying to replicate these designs in code. Everything that we style on the web must conform within the rules and properties defined by CSS. This is easy when creating simple web components, but as soon as we stray away from these bounds, life starts to become very difficult and the code becomes hacky. If you have ever attempted to create a simple triangle in CSS, you’ll know what I mean (spoiler alert: It involves having to do trigonometry in CSS, a skill that I never thought I would need outside of 10th grade mathematics). I have encountered a number of instances where the design is simply not possible within the current bounds of CSS, the code simply becomes so hacky that it isn’t worth implementing. In cases like this, the only option you really have is to export the particular component as an image or svg and use that instead. I have to admit this always upsets me a little, simply because images feel like a compromise, they take a long time to load, they’re not really responsive, and - worst of all - they disrupt your web developer street cred.

Right now, CSS is basically the only tool we have for telling the browser how we want HTML elements to appear on a page. As a developer, we write specific rules that the browser interprets to produce a set of pixels on the page (often browsers interpret these rules in different ways, i.e. Chrome does things differently to Safari). This means that the developer is restricted to the rules and boundaries defined by the CSS specification. That said, there has been a recent push to move towards providing developers access to a lot of the low level APIs within the browser to allow the developer more control in terms of styling, layout and animation.

Enter Houdini: the web initiative aimed at enabling developers gain more control over the fundamentals of how the browser renders the web page. This means that we can break out of the bounds of CSS and write code that allows us to directly control how the browser creates individual pixels on the page. Rather than telling the browser how we want it to render a particular element through declarative rules, we instead are able to write code that produces the actual pixels on the page. While I write this, Houdini is only partially implemented on Google Chrome since version 65 with other browsers set to follow suit. You can follow the current status of the project using this handy website created by Surma.

In this article, we’ll be exploring one component of the Houdini project, the CSS Paint API. This api allows the developer to programmatically produce a set of pixels anywhere that CSS expects a background image. Similar to how you would define a linear-gradient as the background image, you can now specify your own special paint function that is evaluated in code and rendered to the page. Essentially allowing you to write a function that produces any set of pixels you can dream of.

With CSS Paint API, we define what are known as ‘paint worklets’, which are basically just JavaScript modules which tell the browser how it should render the background. The first step in defining a custom paint worklet is to create a class and register it to the browser with a name.

The first argument passed in to this paint function is ‘ctx’. This is a HTML Canvas context. If have not heard of HTML Canvas, I recommend you read this quick article before reading on. The second argument is the geometry. It contains information that tells us how big the element should be and where it’s located on the page, i.e. width and height and x, y positions. Finally, the third argument provides us with access to any other CSS properties defined by this element, such as text colour, border radius or any custom properties. Now that we have defined the paint worklet we have to include it in HTML and tell CSS that we want to use our fancy painter as the background image of our element:

Let’s start with a simple example, we will draw two circles in the centre of the div:

Pretty simple, right? Now, let's try to draw a more complex shape:

We can also pass variables into our paint worklet. Let’s try to change the colour of this element. First we just create a variable in the css, then we can access that from our paint worklet code:


By using these attributes you’re able to pass in any value that your paint worklet can read. The possibilities for this are endless, you can imagine you could customise any attribute about your component, from colour to size to more advanced properties of your choosing.

These attributes are very powerful when it comes to animation too. Should I want to animate the colour of my component, all I’d need to do is define a keyframe animation in my CSS, modify the ‘--shape-color’ property and my component will update as required. The best part about this is that the browser will decide the most optimal way to re-render your component, so you don't need to worry about manually re-rendering your component on update.

I hope you now understand the basics of this new amazingly powerful primitive API. While these examples have been very basic, the sky's the limit with this new API. I personally cannot wait for it to be properly supported by more browsers - it’s definitely a step in the right direction toward the evolution of the web.

If you would like to find out more about the above, I highly recommend checking out this awesome talk from the Chrome Developer Summit in late 2018

About Me

My name is Paul, I am the technical lead at Lumi Finance, a fintech startup with the mission to empower small businesses across Australia through access to capital to grow. At Lumi we are using data and technology as the core of our business to bring access to funding with speed, confidence and at the best price. We are always looking for software engineers to join our rapidly growing team, if you are interested in joining please shoot me a message on my LinkedIn here.

Coder Academy is the most loved Australian Coding Bootcamp provider according to Course Report. Our accredited coding Bootcamps will set you up for a new career in tech via our Fast Track Bootcamp or our Flex Track Bootcamp . Our immersive courses help students acquire in-demand skills through hands on, project-based training by industry educators and experts.

Now enrolling | domestic & international students in Sydney, Melbourne & Brisbane! Study now, pay later with FEE-HELP!