CSS paint() Function
The CSS paint() function generates custom images using JavaScript code registered through the CSS Painting API.
It allows developers to create backgrounds, borders, and other graphical effects without using traditional image files. The function references a registered paint worklet, which draws the image using a canvas-like rendering context. Browser support is limited, so a fallback should be provided.
Syntax
paint(name)
paint(name, arguments)
The first argument identifies a registered paint worklet. Additional arguments can be supplied when the worklet declares support for them.
For example:
background-image: paint(custom-pattern);
background-image: paint(custom-border);
background-image: paint(custom-pattern, red);
The names custom-pattern and custom-border must match names registered by JavaScript.
The function produces a CSS <image> value and can be used with compatible properties.
For example:
.box {
background-image: paint(custom-pattern);
}
The browser invokes the registered paint worklet to generate the background image.
The paint() function does not contain drawing instructions itself. Those instructions are defined in JavaScript.
Parameters
The paint() function accepts a required worklet name and optional additional arguments.
| Parameter | Description |
|---|---|
name |
The registered name of the paint worklet that generates the image. |
arguments |
Optional comma-separated values passed to the worklet when its inputArguments definition supports them. |
For example:
paint(dot-pattern)
This references a paint worklet registered with the name dot-pattern.
Another example supplies an argument:
paint(dot-pattern, 12px)
The worklet must declare and process the additional argument for this form to work.
The worklet name is a CSS identifier, not a quoted string or a JavaScript filename.
For example, the following is not the correct way to reference a worklet:
background-image: paint("dot-pattern.js");
Instead, register the worklet using JavaScript and reference its registered name in CSS.
How It Works
The paint() function connects CSS image rendering with JavaScript drawing instructions.
The process involves four main steps:
- Create a JavaScript paint worklet containing a drawing class.
- Register the drawing class using
registerPaint(). - Load the worklet using
CSS.paintWorklet.addModule(). - Reference the registered name using the CSS
paint()function.
For example:
.box {
background-image: paint(dot-pattern);
}
When the browser needs to render the background, it uses the registered worklet to generate the image.
The worklet receives information about the image dimensions and the CSS properties it has declared as inputs.
The generated image can respond to changes in element size and supported CSS property values.
The browser manages when the paint code runs. Developers do not manually call the worklet's drawing method for each frame.
CSS Painting API
The CSS Painting API is part of the CSS Houdini collection of browser APIs.
It allows custom JavaScript drawing code to generate images that can be used in CSS.
For example, a generated image can be applied as a background:
.panel {
background-image: paint(custom-pattern);
}
It can also be used with properties that accept CSS image values, subject to the property's requirements and browser support.
The Painting API provides a specialized drawing context similar to the HTML Canvas 2D context, but it does not expose every Canvas API feature.
Unlike a regular canvas element, a paint worklet generates an image for use within CSS styling.
The API is designed for custom rendering rather than general DOM manipulation.
Paint worklets run in a restricted environment and cannot directly access the document or ordinary browser window objects.
Paint Worklets
A paint worklet is a JavaScript module containing a class that defines how an image should be drawn.
For example:
class SolidPattern {
paint(ctx, size) {
ctx.fillStyle = "#0066cc";
ctx.fillRect(0, 0, size.width, size.height);
}
}
registerPaint("solid-pattern", SolidPattern);
The class contains a paint() method that draws a blue rectangle.
The registerPaint() function associates the class with the name solid-pattern.
The registered name can then be used in CSS:
.box {
background-image: paint(solid-pattern);
}
The JavaScript method named paint() is different from the CSS paint() function.
The JavaScript method performs the drawing, while the CSS function requests the generated image.
Registering Paint Worklets
Paint worklets must be registered before their generated images can be rendered.
First, create a JavaScript module named pattern.js:
class PatternPainter {
paint(ctx, size) {
ctx.fillStyle = "#0066cc";
ctx.fillRect(0, 0, size.width, size.height);
}
}
registerPaint("custom-pattern", PatternPainter);
Next, load the module from the main document's JavaScript:
if ("paintWorklet" in CSS) {
CSS.paintWorklet.addModule("pattern.js");
}
The addModule() method loads the JavaScript module into the paint worklet environment.
After registration, the worklet can be referenced in CSS:
.box {
background-image: paint(custom-pattern);
}
The filename and registered worklet name serve different purposes.
The filename identifies the JavaScript module to load. The registered name identifies the drawing class used by CSS.
Worklet modules must be loaded from an appropriate secure environment, such as HTTPS or a supported localhost development server.
Opening the example directly using a file:// URL may not work.
The paint() Method
A paint worklet class defines a JavaScript method that receives the drawing context, image dimensions, computed CSS properties, and optional arguments.
Its general structure is:
paint(ctx, size, properties, args) {
// Drawing instructions
}
The parameters are:
| Parameter | Description |
|---|---|
ctx | The drawing context used to create the image. |
size | An object containing the image's width and height. |
properties | A map of the computed CSS property values declared by the worklet. |
args | Optional values supplied through the CSS paint() function. |
For example:
class CirclePainter {
paint(ctx, size) {
const radius = Math.min(size.width, size.height) / 4;
ctx.fillStyle = "#0066cc";
ctx.beginPath();
ctx.arc(
size.width / 2,
size.height / 2,
radius,
0,
Math.PI * 2
);
ctx.fill();
}
}
registerPaint("circle-pattern", CirclePainter);
This worklet draws a blue circle centered within the generated image.
The circle size is calculated from the available image dimensions.
The worklet can be used as a CSS background:
.box {
background-image: paint(circle-pattern);
}
The browser determines the dimensions supplied to the worklet based on the CSS image's rendering context.
Using CSS Custom Properties
Paint worklets can read selected computed CSS properties, including custom properties.
The worklet must declare the properties it uses through a static inputProperties getter.
For example:
class ColorPainter {
static get inputProperties() {
return ["--paint-color"];
}
paint(ctx, size, properties) {
const color = properties.get("--paint-color").toString().trim();
ctx.fillStyle = color;
ctx.fillRect(0, 0, size.width, size.height);
}
}
registerPaint("color-pattern", ColorPainter);
The worklet reads the computed value of --paint-color and uses it as the fill color.
In CSS:
.box {
--paint-color: #0066cc;
background-image: paint(color-pattern);
}
.box:hover {
--paint-color: #00a896;
}
The background can be repainted when the declared custom property changes.
Custom properties allow the same worklet to generate different results for different elements.
Provide valid property values and handle missing or unexpected values in production worklets.
Passing Function Arguments
The CSS paint() function can accept additional arguments when the registered worklet declares their expected syntax.
For example:
background-image: paint(circle-color, blue);
The worklet must declare its argument types using inputArguments.
For example:
class CircleColorPainter {
static get inputArguments() {
return ["<color>"];
}
paint(ctx, size, properties, args) {
const color = args[0].toString();
const radius = Math.min(size.width, size.height) / 4;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(
size.width / 2,
size.height / 2,
radius,
0,
Math.PI * 2
);
ctx.fill();
}
}
registerPaint("circle-color", CircleColorPainter);
The worklet accepts a CSS color argument and uses it to draw the circle.
The corresponding CSS is:
.blue-circle {
background-image: paint(circle-color, blue);
}
.red-circle {
background-image: paint(circle-color, red);
}
The two elements use the same drawing class but supply different colors.
Support for additional paint function arguments can vary by browser implementation. Custom properties are another way to pass values to a paint worklet.
Responsive Generated Images
A paint worklet can use the available image dimensions to generate graphics that adapt to the size of the rendered image.
For example:
class DiagonalPainter {
paint(ctx, size) {
ctx.strokeStyle = "#0066cc";
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.width, size.height);
ctx.stroke();
}
}
registerPaint("diagonal-line", DiagonalPainter);
The line extends from the top-left corner to the bottom-right corner of the generated image.
In CSS:
.box {
width: 100%;
min-height: 150px;
background-image: paint(diagonal-line);
}
When the image dimensions change, the browser can generate a new drawing using the updated width and height.
This makes paint worklets useful for graphics that need to adapt to different layout sizes.
The worklet draws within the image dimensions supplied by the browser. It does not directly control the element's layout size.
Fallbacks and Feature Detection
Because CSS Painting API support is limited, provide a standard CSS background or another supported image as a fallback.
For example:
.box {
background-color: #0066cc;
background-image: linear-gradient(135deg, #0066cc, #00a896);
}
@supports (background-image: paint(custom-pattern)) {
.box {
background-image: paint(custom-pattern);
}
}
The gradient is used when the browser does not support the custom paint image.
The @supports rule checks whether the browser recognizes the CSS declaration.
JavaScript feature detection can also be used:
if ("paintWorklet" in CSS) {
CSS.paintWorklet.addModule("pattern.js");
}
This checks for the Painting API before attempting to load the module.
Feature detection does not guarantee that a worklet has loaded successfully or that its drawing code is valid.
For production use, handle module loading failures and retain a useful fallback.
Also remember that the worklet name must be registered before the browser can render the generated image.
Example
This example creates a repeating dot pattern using a JavaScript paint worklet.
It requires a separate JavaScript file and a browser that supports the CSS Painting API.
HTML:
<div class="paint-demo">
Custom Dot Pattern
</div>
<script>
if ("paintWorklet" in CSS) {
CSS.paintWorklet.addModule("dot-pattern.js");
}
</script>
CSS:
.paint-demo {
display: flex;
align-items: center;
justify-content: center;
min-height: 180px;
padding: 20px;
background-color: #e9f2ff;
background-image: radial-gradient(#0066cc 2px, transparent 2px);
background-size: 20px 20px;
color: #111111;
text-align: center;
font-size: 16px;
font-weight: bold;
}
@supports (background-image: paint(dot-pattern)) {
.paint-demo {
background-image: paint(dot-pattern);
background-size: auto;
}
}
JavaScript - dot-pattern.js:
class DotPatternPainter {
paint(ctx, size) {
const spacing = 20;
const radius = 2;
ctx.fillStyle = "#0066cc";
for (let x = 10; x < size.width; x += spacing) {
for (let y = 10; y < size.height; y += spacing) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
}
}
}
}
registerPaint("dot-pattern", DotPatternPainter);
The JavaScript worklet draws small blue circles across the available image area.
The CSS paint(dot-pattern) function references the registered drawing class.
In browsers without Painting API support, the CSS radial gradient provides a similar dotted background.
The generated pattern is decorative. The text remains ordinary HTML content.
Browser Result:
The following preview demonstrates the fallback appearance. It does not execute the paint worklet.
To test the actual worklet, save the JavaScript in dot-pattern.js, load the HTML page through HTTPS or localhost, and use a compatible browser.
Custom Properties Example
This example creates a custom striped background that reads its color from a CSS custom property.
CSS:
.paint-stripes {
--stripe-color: #0066cc;
min-height: 150px;
background-color: #ffffff;
background-image: repeating-linear-gradient(
45deg,
#0066cc 0 10px,
#ffffff 10px 20px
);
}
@supports (background-image: paint(stripe-pattern)) {
.paint-stripes {
background-image: paint(stripe-pattern);
}
}
JavaScript - stripe-pattern.js:
class StripePainter {
static get inputProperties() {
return ["--stripe-color"];
}
paint(ctx, size, properties) {
const color = properties.get("--stripe-color").toString().trim();
const stripeWidth = 10;
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, size.width, size.height);
ctx.fillStyle = color;
for (let x = -size.height; x < size.width; x += stripeWidth * 2) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x + stripeWidth, 0);
ctx.lineTo(x + size.height + stripeWidth, size.height);
ctx.lineTo(x + size.height, size.height);
ctx.closePath();
ctx.fill();
}
}
}
registerPaint("stripe-pattern", StripePainter);
JavaScript - Load the Worklet:
if ("paintWorklet" in CSS) {
CSS.paintWorklet.addModule("stripe-pattern.js");
}
The worklet reads the --stripe-color property and uses it when drawing the stripes.
Different elements can use different custom property values:
.stripe-blue {
--stripe-color: #0066cc;
}
.stripe-green {
--stripe-color: #008866;
}
This allows one registered worklet to generate multiple color variations.
The CSS gradient provides a fallback, although the custom property only changes the worklet-generated stripes in this simplified example.
Common Uses
The paint() function is useful for generating decorative images directly from JavaScript drawing instructions.
| Use | Description |
|---|---|
| Custom backgrounds | Generate patterns, shapes, and other decorative background images. |
| Procedural patterns | Create dots, stripes, grids, and repeated geometric designs. |
| Responsive graphics | Draw images based on the available rendering dimensions. |
| Custom borders | Generate image-based border effects where supported. |
| Design systems | Reuse registered drawing logic across multiple elements. |
| Custom property effects | Adjust generated graphics using CSS values. |
| Experimental rendering | Create specialized graphical effects not directly available through standard CSS. |
For simple shapes, gradients, and patterns, standard CSS is usually easier and more widely supported.
Use paint() when custom drawing logic provides a meaningful advantage over existing CSS features.
Limitations
The paint() function has several important requirements and limitations.
- It requires a registered JavaScript paint worklet.
- Browser support is limited and is not consistent across all major browser engines.
- The Painting API requires a secure context.
- The worklet must be loaded before its generated image can be rendered.
- Paint worklets execute in a restricted JavaScript environment without direct DOM access.
- The drawing context supports a subset of familiar Canvas 2D operations.
- Additional function arguments require a compatible
inputArgumentsdeclaration. - Custom properties must be declared through
inputPropertiesto be supplied to the worklet. - Generated images do not replace semantic HTML content or accessible text.
- Complex drawing operations can increase rendering costs.
- A CSS or image fallback is recommended for unsupported browsers.
For example, this CSS requires a registered worklet:
background-image: paint(custom-pattern);
The declaration alone does not create a custom image.
The following is also not a valid way to load a worklet:
background-image: paint("pattern.js");
Worklet modules are loaded through JavaScript, not directly from a filename supplied to paint().
For production websites, provide a usable fallback and verify the feature in the browsers your audience uses.
Browser Support
The CSS paint() function and CSS Painting API have Limited availability and are not Baseline across major browsers.
Chromium-based browsers, including Chrome and Edge, support CSS Paint Worklets. Support is not consistently available in Firefox and Safari.
For example:
.box {
background-color: #0066cc;
background-image: paint(custom-pattern);
}
In browsers that do not support the function, the unsupported image declaration is ignored, leaving the background color.
A more complete fallback uses a standard CSS image:
.box {
background-image: linear-gradient(135deg, #0066cc, #00a896);
}
@supports (background-image: paint(custom-pattern)) {
.box {
background-image: paint(custom-pattern);
}
}
The custom worklet must also be registered and loaded successfully.
For current compatibility information, visit Can I Use? and search for CSS Painting API.
Specifications
The paint() function is defined in the CSS Painting API Level 1 .
The specification defines custom paint images, paint worklet registration, drawing contexts, and how worklets receive CSS property values.
Related CSS features include the background-image property, border-image-source property, CSS custom properties, and the @supports rule.
