CSS Houdini Paint Worklet Generator
Generate registerPaint() worklet code and CSS for 10 patterns — checkerboard, stripes, dots, waves, noise, circles, triangles, confetti, gradient mesh, and crosshatch
Select Pattern
Live Preview
Browser Support
Polyfill: For Firefox and Safari, add css-houdini-polyfill or use a @supports (background: paint(id)) fallback.
Parameters — Checkerboard
--checkerboard-color1 — First square color
--checkerboard-color2 — Second square color
--checkerboard-size — Size of each square in pixels
class CheckerboardPainter {
static get inputProperties() {
return [
'--checkerboard-color1',
'--checkerboard-color2',
'--checkerboard-size',
];
}
paint(ctx, geom, properties) {
const color1 = properties.get('--checkerboard-color1').toString().trim() || '#1e293b';
const color2 = properties.get('--checkerboard-color2').toString().trim() || '#f1f5f9';
const size = parseInt(properties.get('--checkerboard-size').toString()) || 24;
const cols = Math.ceil(geom.width / size) + 1;
const rows = Math.ceil(geom.height / size) + 1;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
ctx.fillStyle = (row + col) % 2 === 0 ? color1 : color2;
ctx.fillRect(col * size, row * size, size, size);
}
}
}
}
registerPaint('checkerboard', CheckerboardPainter);How to use this worklet:
- Save the code above as
checkerboard.json your server - In your main JS:
CSS.paintWorklet.addModule('checkerboard.js'); - Switch to the CSS tab to see how to apply it
How Checkerboard Works
The checkerboard worklet iterates over a grid of rows and columns, filling each cell with alternating colors based on whether the sum of the row and column indices is even or odd. The --checker-size custom property controls the cell size in pixels.
CSS Custom Properties
| Property | Default |
|---|---|
| --checkerboard-color1 | #1e293b |
| --checkerboard-color2 | #f1f5f9 |
| --checkerboard-size | 24 |
| background | paint(checkerboard) |
You need a subtle dot pattern background that tiles infinitely, responds to CSS custom properties for color and spacing, and doesn’t require a PNG file. CSS gradients can fake simple patterns, but a checkerboard with rounded corners, a wave pattern, or confetti particles require increasingly absurd gradient stacks — or a single Paint Worklet that draws exactly what you want using the Canvas API.
Why This Tool (Not Writing the Worklet from Scratch)
CSS Houdini Paint Worklets require a specific class structure (static get inputProperties, paint(ctx, geom, properties)), a separate JS file registered with CSS.paintWorklet.addModule(), CSS custom properties for parameters, and the paint() reference in your stylesheet. That’s 4 moving pieces before you draw a single pixel. This tool generates the complete worklet JS and CSS for 10 pattern types (checkerboard, stripes, dots, waves, noise, circles, triangles, confetti, gradient mesh, crosshatch) with customizable colors, sizes, and angles. Everything runs in your browser.
What Is the CSS Houdini Paint API?
The CSS Houdini Paint API — formally part of the CSS Painting API Level 1 specification — lets you define custom background images and border images using JavaScript that runs at paint time. Instead of writing a static image or complex CSS gradient chains, you register a paint worklet: a small JavaScript class that draws directly onto a canvas-like context every time the browser needs to paint an element.
// my-pattern.js (the worklet file)
class CheckerboardPainter {
static get inputProperties() {
return ['--checker-size', '--checker-color1', '--checker-color2'];
}
paint(ctx, geom, properties) {
const size = parseInt(properties.get('--checker-size')) || 20;
// ... draw on ctx using Canvas 2D API
}
}
registerPaint('checkerboard', CheckerboardPainter);
// Your main script
CSS.paintWorklet.addModule('my-pattern.js');
/* Your CSS */
.element {
--checker-size: 24;
--checker-color1: #1e293b;
--checker-color2: #f1f5f9;
background: paint(checkerboard);
}
The result: a native, hardware-accelerated background pattern that responds to CSS custom property changes in real time — no images, no JavaScript DOM manipulation at paint time.
How CSS Paint Worklets Work
A paint worklet has three key parts:
1. static get inputProperties()
Returns an array of CSS custom property names the worklet needs to read. This tells the browser which CSS variables to pass to the paint() method. Only declaring the properties you need avoids unnecessary repaints.
2. The paint(ctx, geom, properties) Method
ctx— aPaintRenderingContext2D, a subset of the standard Canvas 2D context. Supports fills, strokes, gradients, arcs, transforms, and global alpha — but notdrawImage, text, shadows, or some other advanced features.geom— aPaintSizeobject withwidthandheightmatching the painted element’s content box.properties— aStylePropertyMapReadOnlyfor reading the declaredinputPropertiesvalues.
3. registerPaint('name', ClassName)
Registers the worklet class under a name that matches the paint() CSS function call.
CSS Custom Properties as Parameters
The real power of Houdini Paint is that CSS custom properties become live, animatable parameters. Because CSS custom properties are inherited and can be changed by media queries, pseudo-classes, or JavaScript, your paint worklet automatically re-renders whenever a property changes.
.card {
--dot-color: #6366f1;
--dot-spacing: 32;
background: paint(dots);
transition: --dot-spacing 0.3s ease; /* animate the spacing! */
}
.card:hover {
--dot-spacing: 20;
}
Note: Animating CSS custom properties in Houdini requires registering them with CSS.registerProperty() to give the browser a type and initial value for interpolation.
Pattern Types and When to Use Them
Checkerboard
The classic alternating square grid. Use it for:
- Debug overlays to verify layout boxes
- Transparent layer indicators (like Photoshop’s transparency grid)
- Bold geometric backgrounds for headers or hero sections
The checkerboard worklet is the simplest to understand — pure grid math with no transforms.
Stripes
Diagonal or straight stripes controlled by width, gap, and angle. Use it for:
- Construction/warning tape aesthetics
- “Coming soon” placeholder overlays
- Background textures for pricing cards or banners
The rotation is achieved by translating to the element center, rotating the context, drawing horizontal rectangles across the full diagonal, then restoring — a common canvas rotation technique.
Dots / Polka Dots
Circles on a regular grid. Use it for:
- Retro poster aesthetics
- Progress or loading screen backgrounds
- Decorative section separators
Increasing the dot radius beyond half the spacing causes dots to overlap, creating a very different visual.
Noise / Grain
Random grain for analog texture. Use it for:
- Vintage photo or film aesthetics
- Subtle texture on flat-color backgrounds to reduce banding
- Paper or concrete material effects
Because Math.random() is called fresh on every paint, the grain pattern shifts when the element is resized — mimicking real film grain.
Waves
Sine wave lines stacked vertically. Use it for:
- Ocean, water, or audio waveform aesthetics
- Organic background textures
- Decorative dividers between page sections
The frequency controls how many complete sine cycles fit across the element width; amplitude controls the peak-to-trough height.
Circles
Circle outlines on a grid. Unlike filled dots, outlines create an interlocking lattice when the diameter exceeds the spacing. Use it for:
- Subtle geometric backgrounds
- Bubble or honeycomb effects
- Elegant decorative patterns
Triangles
Scattered triangles with a seeded random layout. Use it for:
- Low-poly geometric aesthetics
- Abstract art backgrounds
- Gaming or tech brand visuals
The seed parameter makes the layout reproducible — the same seed always places the same triangles, so your design stays consistent across page loads.
Gradient Mesh
A grid of cells, each filled with a linear gradient blending between three colors. Use it for:
- Vibrant, colorful hero backgrounds
- Aurora borealis or prism effects
- Bold gradients that feel more dynamic than simple linear/radial
Confetti
Scattered rectangular confetti pieces in three colors. Use it for:
- Celebration or party themes
- Promotional landing pages
- Fun, energetic backgrounds
Like triangles, confetti uses a seeded PRNG for reproducibility.
Crosshatch
Two sets of parallel diagonal lines at 45° and 135°. Use it for:
- Technical drawing or blueprint aesthetics
- Pencil-shading texture effects
- Subtle, low-contrast background textures
Browser Support
The CSS Houdini Paint API is supported in Chrome and Chromium-based browsers (Edge, Opera, Brave) since 2018. Firefox and Safari do not currently support it.
| Browser | Support | Notes |
|---|---|---|
| Chrome 65+ | Full | The reference implementation |
| Edge 79+ | Full | Chromium-based Edge |
| Firefox | None | Behind dom.paintWorklet.enabled flag, not shipped |
| Safari | None | Not implemented in WebKit |
| Chrome for Android | Full | Supported on mobile Chrome |
Polyfill Strategy
For Firefox and Safari compatibility, use CSS Houdini Polyfill (css-houdini-polyfill on npm) or the Houdini.how polyfill loader. The polyfill emulates the worklet API using a Web Worker and falls back to canvas rendering injected as a data URI background.
<script>
// Progressive enhancement: load polyfill only when needed
if (!('paintWorklet' in CSS)) {
import('https://unpkg.com/css-houdini-polyfill/dist/css-houdini.js');
}
</script>
Alternatively, provide a CSS fallback using @supports:
.element {
/* Fallback for unsupported browsers */
background-color: #f1f5f9;
background-image: linear-gradient(45deg, #1e293b 25%, transparent 25%);
}
@supports (background: paint(id)) {
.element {
background: paint(checkerboard);
}
}
Performance Considerations
CSS Houdini Paint worklets run off the main thread in a dedicated worklet scope, similar to Web Workers. This means:
- Heavy paint operations don’t block JavaScript execution
- The browser can cache painted output and only re-invoke the worklet when
inputPropertieschange - Worklets compose with GPU layers, enabling smooth CSS animations
Keep paint worklets fast: avoid heavy loops on large elements, and use inputProperties to minimize unnecessary repaints. The browser only re-invokes paint() when a declared input property changes.
FAQ
Do I need to serve the worklet file from my own server?
Yes — CSS.paintWorklet.addModule(url) requires a URL that the browser can fetch. The worklet file must be served from the same origin or with appropriate CORS headers. You cannot use inline scripts or data URIs directly with addModule.
Can I use Houdini Paint with CSS animations?
Yes, with CSS custom property registration. Register the property type with CSS.registerProperty() first:
CSS.registerProperty({
name: '--dot-spacing',
syntax: '<number>',
inherits: false,
initialValue: '32',
});
Once typed, CSS transitions and animations can interpolate the value, and the worklet re-paints at each frame.
Can paint worklets draw text or images?
The PaintRenderingContext2D does not support fillText, drawImage, or createPattern. It is a deliberate subset of Canvas 2D that excludes operations requiring access to external resources (which would create security issues in a worklet context).
What happens when the paint worklet throws an error?
If the paint() method throws, the browser treats the background as transparent for that frame. It will retry on the next paint cycle, so a temporary error won’t permanently break the element.
Is the Houdini Paint API the same as CSS Houdini?
CSS Houdini is an umbrella term for several low-level APIs that expose CSS engine internals to JavaScript. The Paint API is one of them. Others include the Layout API (custom layout algorithms), the Animation Worklet (off-thread animation), the Properties and Values API (CSS.registerProperty), and Typed OM (element.attributeStyleMap).