CSS Houdini Paint API explained
Tags: webdev, javascript, css, tutorial
Published: 2020-07-13 05:51:43

<small>This post has been [originally published on Smashing Magazine](https://www.smashingmagazine.com/2020/03/practical-overview-css-houdini/) and I decided to split it into parts to make it more digestible. I'll be converting the post into markdown and publish a part of it on DEV every week. If you want to read it right away in its entirety, feel free to read it on Smashing Magazine until all parts are available on DEV. Thank you.</small>

<hr/>

## Paint API

The Paint API allows developers to use JavaScript functions to draw directly into an element’s background, border, or content using [2D Rendering Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D), which is a subset of the HTML5 Canvas API. Paint API uses Paint Worklet to draw an image that dynamically responds to changes in CSS (changes in CSS variables, for example). Anyone familiar with Canvas API will feel right at home with Houdini’s Paint API.

There are several steps required in defining a Paint Worklet:
<ol>
    <li>Write and register a Paint Worklet using the <code>registerPaint</code> function</li>
    <li>Call the Worklet in HTML file or main JavaScript file using <code>CSS.paintWorklet.addModule</code> function</li>
    <li>Use the <code>paint()</code> function in CSS with a Worklet name and optional input arguments.</li>
</ol>

Let’s take a look at the `registerPaint` function which is used to register a Paint Worklet and define its functionality.

```js
registerPaint("paintWorketExample", class {
  static get inputProperties() { return ["--myVariable"]; }
  static get inputArguments() { return ["<color>"]; }
  static get contextOptions() { return {alpha: true}; }

  paint(ctx, size, properties, args) {
    /* ... */
  }
});
```


The `registerPaint` function consists of several parts:
<ul>
<li>
    <code>inputProperties</code>:
    An array of CSS custom properties that the Worklet will keep track of. This array represents dependencies of a paint worklet.
</li>
<li>
    <code>inputArguments</code>:
    An array of input arguments that can be passed from <code>paint</code> function from inside the CSS.
</li>
    <li><code>contextOptions</code>: allow or disallow opacity for colors. If set to <code>false</code>, all colors will be displayed with full opacity.</li>
<li>
    <code>paint</code>: the main function that provides the following arguments:
<ul>
<li>
        <code>ctx</code>: 2D drawing context, almost identical to Canvas API’s 2D drawing context.</li>
<li>
        <code>size</code>: an object containing the width and height of the element. Values are determined by the layout rendering process. Canvas size is the same as the actual size of the element.</li>
<li>
        <code>properties</code>: input variables defined in <code>inputProperties</code>
</li><li>
        <code>args</code>: an array of input arguments passed in <code>paint</code> function in CSS
</li>
</ul></li>
</ul>

After the Worklet has been registered, it needs to be invoked in the HTML file by simply providing a path to the file.

```js
CSS.paintWorklet.addModule("path/to/worklet/file.js");
```

Any Worklet can also be added from an external URL (from a Content Delivery Network, for example) which makes them modular and reusable.
```js
CSS.paintWorklet.addModule("https://url/to/worklet/file.js");
```

After the Worklet has been called, it can be used inside CSS using the `paint` function. This function accepts the Worklet’s registered name as a first input argument and each input argument that follows it is a custom argument that can be passed to a Worklet (defined inside Worklet’s `inputArguments`). From that point, the browser determines when to call the Worklet and which user actions and CSS custom properties value change to respond to.

```css
.exampleElement {
  /* paintWorkletExample - name of the worklet
     blue - argument passed to a Worklet */
  background-image: paint(paintWorketExample, blue);
}
```
### Example

The following example showcases Paint API and general Worklet reusability and modularity. It’s using the ripple Worklet directly from [Google Chrome Labs repository](https://github.com/GoogleChromeLabs/houdini-samples/blob/master/paint-worklet/ripple/paintworklet.js) and runs on a different element with different styles. Complete source code is available on the [example repository](https://github.com/codeAdrian/houdini-examples/tree/master/paint-api-example).

<figure>
![Ripple effect example (uses Ripple Worklet by Google Chrome Labs) ](https://dev-to-uploads.s3.amazonaws.com/i/n6tsh7eajewzhl6qt0i5.gif)
<figcaption>Ripple effect example (uses Ripple Worklet by Google Chrome Labs) </figcaption>
</figure>

### Feature detection

```js
if ("paintWorklet" in CSS) {
  /* ... */
}
```

```css
@supports(background:paint(paintWorketExample)){
  /* ... */
}
```

### W3C Specification Status

[Candidate recommendation](https://drafts.css-houdini.org/css-paint-api/): stable working draft ready for implementation

### Browser Support

- Google Chrome: Supported
- Microsoft Edge: Supported
- Opera Browser: Supported
- Firefox: Not supported
- Safari: Not supported

<small>
Data source: [Is Houdini Ready Yet?](https://ishoudinireadyyet.com/)
</small>

<hr/>
These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.

<a href="https://www.buymeacoffee.com/ubnZ8GgDJ" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-red.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>

Thank you for taking the time to read this post. Keep an eye out for the next part in the series. If you've found this useful, please give it a ❤️ or 🦄, share and comment.