Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Worley Noise Shader for React

Worley noise drawn on the GPU: a dense field of dark cells whose seams glow as a fine web, drifting slowly. For React with Base UI, when a hero, a card or a section wants an organic, cellular texture in your own colors.

Framework

Worley Noise Shader draws cellular noise: a random point in every cell of a grid, and each pixel shaded by its distance to the nearest one. That makes a dense field of dark pits, each brightening in a soft cone toward its neighbors, and a glowing web of seams where two cells meet. Every point circles slowly inside its cell while the whole field drifts down and to the left, so the web ripples in place. A WGSL fragment shader on WebGPU draws every pixel each frame, for under half a millisecond of GPU time.

Use it as a texture behind a hero, a stat card or a section header, in monochrome or in your theme's colors. For glowing, embossed cells in hot colors, use Three-Tap Voronoi Shader; for flowing contour lines over a blended cell field, Smooth Voronoi Contours Shader; for lit glass cells in plain CSS, the Voronoi background. It runs only while it's on screen, holds a still frame with reduced motion, and shows a CSS grid of the same pits in browsers without WebGPU.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { WorleyNoiseShader } from "@/components/ui/worley-noise-shader";
<WorleyNoiseShader className="flex min-h-[32rem] items-center justify-center">
  <div className="rounded-xl bg-black/60 px-8 py-6">
    <h1 className="text-5xl font-semibold text-white">Ship faster</h1>
  </div>
</WorleyNoiseShader>

Worley Noise Shader is a container: your content sits on top of the texture, in normal flow. It fills its own box, so give it a height or content. For a backdrop behind the whole page, render it empty with className="fixed inset-0 -z-10". The texture runs from near black to near white every few pixels, so no text color reads on it bare: put copy on a panel, like the bg-black/60 one above, which keeps white text above 6:1 everywhere.

Demos

Themed

cellColor="var(--background)" and edgeColor="var(--primary)" draw the web in your theme's colors, and they follow a switch between light and dark. contrast={1.6} deepens the pits so more of the box is your background color and the web stands out as thin lines.

Preview width

In a card

cellSize={96} makes the cells three times bigger, so a few large pits fill the card, and contrast={2} keeps the seams thin at that size. It keeps full quality: the seams are fine lines and go soft at half resolution, and a card-sized box costs the GPU next to nothing.

Preview width

Performance

About 0.4 ms of GPU time a frame at 1280×720 on an M2 Max laptop; slower GPUs take several times longer. Each pixel measures its distance to the 9 points around it, whatever the cellSize. Keep quality at 1: the seams are thin lines that blur at half resolution, and the shader is cheap enough not to need it. The animation stops when the shader scrolls out of view, and the browser pauses it in a hidden tab. Every shader on a page shares one GPU device and one frame loop, and vgpu (about 50 KB gzipped) loads only when the first one mounts.

Browser support

The shader needs WebGPU: Chrome and Edge, Safari 26 and later, and Firefox on Windows and macOS. Elsewhere, and until the first frame, the box shows a CSS grid of dark pits in the same two colors, so the layout never jumps. data-state on the root reads unsupported there, if you want to style around it. See Shader Canvas.

Accessibility

The texture is decoration: it's hidden from assistive tech and lets every click through to your content. With reduced motion it draws one still frame and stops. Nothing flashes: the field drifts about 2 pixels a second and the points circle over 10 to 25 seconds. The contrast inside the texture is high and changes every few pixels, so never set text on it bare: a bg-black/60 panel keeps white text above 6:1, and bg-black/70 above 8.9:1. With your own colors, pick a scrim that darkens or lightens both of them.

FAQ

What is Worley noise?

A cellular texture Steven Worley described in 1996. Scatter random points, then shade each pixel by its distance to the nearest point: near a point it's dark, and where two points are equally far, at the seam between two cells, it's brightest. This shader also measures the distance to the second-nearest point, which is what makes the seams glow as a thin web.

How are the points placed?

The screen is cut into a grid, and each cell's integer coordinates go through an integer hash, which gives every cell its own fixed random spot and a slow circle around it. There's no texture and no state, so the pattern is the same on every load, and each pixel only needs to check its own cell and the 8 around it.

How do I change the cell size?

cellSize is the distance between cells in CSS pixels, from 8 to 160, so the texture looks the same on a retina screen and a standard one. Pair large cells with a higher contrast to keep the seams thin.

Can I use my brand colors?

Yes: cellColor is the dark center of each cell and edgeColor the web where they meet. They take any CSS color, var(--primary) included, and follow a theme switch. brightness and contrast set how much of the box each color takes.

Is it animated?

Gently: the field drifts down and to the left at about 2 pixels a second, and every point circles inside its cell, so the web ripples in place. speed scales both, speed={0} or paused holds the current frame, and time draws one fixed moment, in seconds.

Will it slow my page down?

It costs about 0.4 ms of GPU time a frame at 1280×720 on a laptop, the same at any cell size, and the CPU only sets a few numbers each frame. It stops when it's off screen.

Credits

Inspired by a Worley noise shader (opens in a new tab) by Xor on Shadertoy. This is a new shader written from the idea, not a port: its own hash, moving points, seam glow, drift and colors.

API Reference

WorleyNoiseShader

data-slot="worley-noise-shader"

PropType
brightness?number

Overall lightness, from 0.5 (dim) to 2 (washed toward edgeColor). @default 1.

Default
1
cellColor?string

The centres of the cells. Any CSS colour. @default "#0a0a0a".

Default
"#0a0a0a"
cellSize?number

How far apart the cells are, in CSS pixels, from 8 to 160. @default 32.

Default
32
contrast?number

How deep the pits are against the seams, from 0.5 (flat and milky) to 3 (deep, with thin bright seams). @default 1.

Default
1
edgeColor?string

The seams where the cells meet. Any CSS colour. @default "#f5f5f4".

Default
"#f5f5f4"
fallback?string

What shows until the first frame and in browsers without WebGPU: a CSS background value (gradients, sizes, a colour) that looks like the shader. It can use the colour props' --shader-<field> variables.

interactive?boolean

Whether u.pointer follows the pointer. @default false.

paused?boolean

Holds the current frame. @default false.

quality?number

Render scale, from 0.25 to 1, of the screen's pixels (capped at 2×). @default 1.

speed?number

Animation speed: 1 is normal, 0 holds the frame. @default 1.

time?number

A fixed moment, in seconds: draws it and doesn't animate.

render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Forwarded to the part.

All 101 shaders