Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Three-Tap Voronoi Shader for React

A honeycomb of glowing Voronoi cells drawn on the GPU: dark pits rising to white-hot seams, embossed by a light from the top left, under a slow two-color sweep. For React with Base UI, when a hero, a launch or a pricing card wants a warm, tactile pattern.

Framework

Three-Tap Voronoi Shader draws a honeycomb of irregular cells. Each one is a cone: a dark pit in the middle rises through the color to a white-hot seam where it meets its neighbors, and a cool light from the top left catches one side of every seam, so the cells look pressed into the surface. A two-color field sweeps slowly across them, pink to amber by default, and every cell's center wobbles on a small circle, so the walls ripple in place. A WGSL fragment shader on WebGPU draws every pixel each frame, testing only three points per pixel, for about a third of a millisecond of GPU time.

Use it behind a warm hero, a launch page or a pricing card, in its own colors or in your brand's. For a monochrome, finer cell texture, use Worley Noise 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 gradient of the same two colors 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 {
  ThreeTapVoronoiShader,
} from "@/components/ui/three-tap-voronoi-shader";
<ThreeTapVoronoiShader 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>
</ThreeTapVoronoiShader>

Three-Tap Voronoi Shader is a container: your content sits on top of the cells, 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". Every cell runs from a dark pit to a bright seam, 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 5.8:1 everywhere.

Demos

Ice

fromColor and toColor set the two ends of the sweeping color field, here royal blue and aqua, and highlight={1.6} strengthens the light on the seams for a glassier, frozen look. The pits always stay dark, so any pair of mid-to-bright colors works.

Preview width

In a card

The cells are sized to the box's height, about 7 rows at the default scale, so the pattern keeps its proportions in any box. scale={0.7} fits about 5 rows instead, so the cells read as big, glossy tiles at card size.

Preview width

Performance

About 0.3 ms of GPU time a frame at 1280×720 on an M2 Max laptop; slower GPUs take several times longer. Each pixel finds its cell twice (once more for the light), with three distance tests each time, whatever the scale. Keep quality at 1: the seams and highlights are crisp and the shader is cheap enough not to need less. 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 gradient of the same two colors with a darkened edge, 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 pattern 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 cells wobble over 5 to 10 seconds, and the color field takes over a minute to sweep across. The contrast inside each cell is high, so never set text on it bare: a bg-black/60 panel keeps white text above 5.8:1, and bg-black/70 above 8.6:1.

FAQ

What does three-tap mean?

A classic Voronoi pattern checks 9 nearby points per pixel to find the nearest. This one lays the points on a triangle lattice, so a pixel only has to check the 3 corners of the triangle it sits in. That's a third of the work, at the cost of rare, tiny kinks in a wall where a point just outside the triangle would have been nearer. The points barely move from their corners, which keeps those kinks small.

How do I change the cell size?

scale sets how many cells fit: 1 is about 7 rows in the box's height, 0.5 half as many, and 2.5 about 18 small ones. The cells are sized to the box, so a card and a full-screen hero show the same pattern, scaled.

Where does the 3D look come from?

From a second lookup a small step toward the top left. Where the cone rises in that direction, the pixel faces the light and gets a cool highlight, so each seam is lit on one side and not the other. It's a cheap emboss: there's no lighting model and no normal map. highlight sets how strong it is, and 0 turns it off.

Can I change the colors?

Yes: fromColor and toColor are the two ends of the color field that sweeps across the cells. They take any CSS color, var(--primary) included, and follow a theme switch. The pits darken toward each color and the seams brighten toward white, so mid-to-bright colors work best.

Does it move?

Gently: every cell's center circles on a small path, so the walls ripple in place without scrolling, and the color field sweeps diagonally across the box over about a minute. 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.3 ms of GPU time a frame at 1280×720 on a laptop, six distance tests per pixel, and the CPU only sets a few numbers each frame. It stops when it's off screen.

Credits

Inspired by a three-tap Voronoi shader (opens in a new tab) by Shane on Shadertoy. This is a new shader written from the idea, not a port: its own lattice code, hash, cell shading, light, color field and colors.

API Reference

ThreeTapVoronoiShader

data-slot="three-tap-voronoi-shader"

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

fromColor?string

One end of the colour field that sweeps across the cells. Any CSS colour. @default "#f0287a".

Default
"#f0287a"
highlight?number

The embossed light on one side of every seam, from 0 (flat) to 2 (glassy). @default 1.

Default
1
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.

scale?number

How many cells fit: 1 is about 7 rows in the box's height, from 0.5 (big cells) to 2.5 (small). @default 1.

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.

toColor?string

The other end of the colour field. Any CSS colour. @default "#ffab1f".

Default
"#ffab1f"
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