Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Random Grid Subdivision Shader for React

A stained-glass window drawn on the GPU: a grid split into smaller turned grids, down to slivers, leaded in black and lit from behind. For React with Base UI, when a hero, a game or a gallery page wants jewel-toned glass that slowly shifts.

Framework

Random Grid Subdivision Shader draws a stained-glass window that never settles. It starts from a grid of large panes, splits each one with a smaller grid turned to a random angle and offset, and repeats that six levels deep, keeping every level's borders as leading. The result is a mosaic of shards from big panes down to slivers. Each pane is gold toward the top or cobalt toward the bottom, with its own brightness, a nudge of hue and the mottle of antique glass, and a light from behind makes the panes near it glow. A bevel along the leading catches the light on one side. The whole window turns and drifts slowly, and each level slides inside the one above, so the shards shear and re-cut as you watch.

Use it behind a hero, a game, collectibles or gallery page, or anywhere that wants rich, jewel-toned color with structure. For a calmer glass mosaic of rounded cells in your theme's tones, in plain CSS, use the Voronoi background; for flat, interlocking pieces, Minimal Jigsaw Shader. It runs only while it's on screen, holds a still frame with reduced motion, and shows a CSS sketch of leaded glass in the same 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 {
  RandomGridSubdivisionShader,
} from "@/components/ui/random-grid-subdivision-shader";
<RandomGridSubdivisionShader className="flex min-h-[32rem] items-center justify-center">
  <div className="rounded-xl bg-black/60 px-8 py-6 backdrop-blur-sm">
    <h1 className="text-5xl font-semibold text-white">Ship faster</h1>
  </div>
</RandomGridSubdivisionShader>

Random Grid Subdivision Shader is a container: your content sits on top of the glass, 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 window is mostly dark glass, but its bright panes and the leading make it far too busy for bare text: put copy on a frosted panel, like the bg-black/60 backdrop-blur-sm one above, which keeps white text above 5.9:1 everywhere.

Demos

Crystal

grid="hex" cuts every level with hexagons instead of squares, so the shards come out rounder, like crystals. topColor and bottomColor set the two tints, here teal and violet, and lineWidth={5} gives the leading more weight.

Preview width

In a card

The panes scale with the box. uneven={false} splits every pane all the way down, so the glass is an even field of small shards that reads well at card size, and lineWidth={2} keeps the leading in proportion. It keeps full quality: the leading is a few pixels wide and blurs at half resolution.

Preview width

Performance

About 0.5 ms of GPU time a frame at 1280×720 on an M2 Max laptop; slower GPUs take several times longer. Each pixel walks up to six levels of grid, a cell lookup and a few integer hashes each, then one noise lookup for the glass; with uneven, many stop early. Keep quality at 1 if you can: the leading and the bevels go soft at half resolution. 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 sketch of the window, crossing dark lines over a gradient from the top tint to the bottom one, 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 glass 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: no pane changes color in place, the shards only move, and the light behind them drifts over tens of seconds. The window mixes bright and dark glass under a dense web of leading, so never set text on it bare: a bg-black/60 panel keeps white text above 5.9:1, and bg-black/70 above 8.7:1.

FAQ

How does the subdivision work?

The shader starts with a grid of large cells. Inside each cell it lays a smaller copy of the grid, turned to a random angle and shifted by a random offset, both picked by a hash of the cell, and repeats that six levels deep. Every level's cell borders stay drawn, so where the turned grids cross, they cut the panes into shards.

Square or hex?

grid="square", the default, cuts with squares, which gives sharp, angular shards like shattered glass. grid="hex" cuts with hexagons, which gives rounder, crystal-like ones. Either way each level is 1.38 times smaller than the one above.

What does uneven do?

With uneven, the default, a pane from the third level down stops splitting three times in ten, so big panes sit among nests of slivers. uneven={false} splits every pane all six levels, for an even field of small shards.

Why does it look like stained glass?

Three things: the dark leading between panes, with a faint ridge along it; a bevel a few pixels wide inside each pane, lit on the side that faces the light; and the glass itself, tinted per pane, mottled with soft noise and brighter near a light that sits behind the window.

Can I change the colors?

Yes: topColor and bottomColor are the two tints. A pane picks one by where it sits, so the window shades from one to the other with a mixed band in the middle. hueRange sets how far each pane strays from its tint, from 0 to 2, and lineColor sets the leading. They take any CSS color, var(--primary) included, and follow a theme switch.

Will it slow my page down?

It costs about 0.5 ms of GPU time a frame at 1280×720 on a laptop, and the CPU only sets a few numbers each frame. It stops when it's off screen. On a slow GPU, quality={0.5} renders a quarter of the pixels, at the cost of softer leading.

Credits

Inspired by Random Grid Subdivision (opens in a new tab) by Shane on Shadertoy: a mosaic made by repeating a grid inside its own cells at random angles and offsets. This shader is our own code and design, written from scratch: a stained-glass window with two tints, mottled glass, a light behind it and analytic bevels, cut by square or hex grids.

API Reference

RandomGridSubdivisionShader

data-slot="random-grid-subdivision-shader"

PropType
bottomColor?string

The glass toward the bottom. Any CSS color. @default "#2a5bd7".

Default
"#2a5bd7"
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.

grid?keyof typeof GRIDS

The grid each level cuts: squares give sharp, shattered shards, hexagons rounder, crystal ones. @default "square".

Default
"square"
hueRange?number

How far each pane's hue strays from its tint, from 0 (none) to 2. @default 1.

Default
1
interactive?boolean

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

lineColor?string

The leading between panes. Any CSS color. @default "#0c0b10".

Default
"#0c0b10"
lineWidth?number

The leading's width in CSS pixels, from 1 to 8. @default 3.

Default
3
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

The size of the largest panes, from 0.5 to 2. @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.

topColor?string

The glass toward the top. Any CSS color. @default "#f2a33a".

Default
"#f2a33a"
uneven?boolean

Lets some panes stop splitting early, so big panes sit among small ones. @default true.

Default
true
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