CSS Houdini Worklets, Paint API and Font Metrics API
Tags: webdev, javascript, css, tutorial
Published: 2020-06-22 07:00:00

<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/>

## Font Metrics API

The Font Metrics API is still in a very early stage of development, so its specification may change in the future. In its current draft, **Font Metrics API** will provide methods for measuring dimensions of text elements that are being rendered on screen in order to allow developers to affect how text elements are being rendered on screen. These values are either difficult or impossible to measure with current features, so this API will allow developers to create text and font-related CSS features more easily. Multi-line dynamic text truncation is an example of one of those features.

### W3C Specification Status

[Collection of Ideas: no specification draft submitted at the moment](https://drafts.css-houdini.org/font-metrics-api/)

### Browser Support

Unsupported in all browsers

## Worklets

Before moving onto the other APIs, it’s important to explain the Worklets concept. **Worklets** are scripts that run during render and are independent of the main JavaScript environment. They are an extension point for rendering engines. They are designed for parallelism (with 2 or more instances) and thread-agnostic, have reduced access to the global scope and are called by the rendering engine when needed. Worklets can be run only on HTTPS (on production environment) or on localhost (for development purposes).

Houdini introduces following Worklets to extend the browser render engine:

- **Paint Worklet** - Paint API
- **Animation Worklet** - Animation API
- **Layout Worklet** - Layout API

## 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 paint function from inside the CSS.</li>
<li><code>contextOptions</code>: allow or disallow opacity for colors. If set to false, 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 paint 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.

```js
.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>
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rbjjwjggs0p70d9m1law.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: stable working draft ready for implementation](https://drafts.css-houdini.org/css-paint-api/)

### 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.