Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Perspex Web Lattice Shader for React

A wall of faceted perspex cells in a dark web: jewel-like facets that reflect drifting clouds and catch sharp white glints while the cells slowly reshape. For React with Base UI, when a premium, jewellery or luxury page needs a rich, crafted surface behind its copy.

Framework

Perspex Web Lattice Shader draws a wall of perspex cells, each cut into facets like a shallow pyramid, held in a dark web with rounded ridges. The facets reflect drifting clouds of deep blue, the glass color and white, so each one catches its own tone, and sharp white glints slide across them. The seeds of the cells wander, so the cells slowly reshape, and the whole wall turns. A WGSL fragment shader on WebGPU finds each pixel's cell and its distance to the cell's borders, builds the bevelled height and its normal from them, and lights it, with no raymarching.

Use it behind a premium, jewellery or luxury page, or a pricing card that should feel crafted. For a softer glass mosaic in plain CSS, in your theme's colors, use the Voronoi background; for a flat low-poly relief, Facets. It runs only while it's on screen, holds a still frame with reduced motion, and shows a gradient of 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 {
  PerspexWebLatticeShader,
} from "@/components/ui/perspex-web-lattice-shader";
<PerspexWebLatticeShader className="flex min-h-[32rem] items-center justify-center">
  <div className="rounded-xl bg-black/60 px-8 py-6 backdrop-blur">
    <h1 className="text-5xl font-semibold text-white">Ship faster</h1>
  </div>
</PerspexWebLatticeShader>

Perspex Web Lattice Shader is a container: your content sits on top of the wall, 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 wall is dark in both color schemes, so use white text: a large headline reads on it directly, and body text belongs on a panel such as bg-black/60, because the white glints cross everything.

Demos

Emerald

glassColor="#10b981" turns the cells to emerald, density={0.6} makes them fewer and larger, and edges={0} drops the dark lines in the creases, so the facets meet in soft folds of light.

Preview width

In a card

The wall scales with its box. speed={0.5} halves the drift of the cells and the clouds, which suits a premium plan or a membership card. The creases are the detail here, so the card keeps full quality; the shader is light enough for it.

Preview width

Performance

About 1.6 ms of GPU time a frame at 1280×720 on an M2 Max laptop (median 1.2 ms); slower GPUs take several times longer. Each pixel looks at its 3×3 nearest cells, then 5×5 for the exact borders, and lights the result once, with no raymarching. At quality={0.5} the creases and the web's highlights blur, so keep it for slow GPUs. 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 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 colors, a net of web-colored lines over the darkened glass color with patches of blue and two white glints, 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 wall 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 glints slide across the facets as the clouds drift, over seconds. Across the renders we measured, white text reaches 4.5:1 on 95% of the shader, and black text on 5%; the misses are the white glints. White text on a bg-black/60 panel stayed above 5.8:1 on every pixel.

FAQ

It's a flat plane: why does it look 3D?

Every pixel gets a real surface normal. The shader computes the distance to its cell's nearest border, which rises in straight slopes toward the middle, so each cell is a pyramid of flat facets; along the borders the height curves into a rounded ridge, the web. The normal of that height field lights it and aims the reflections.

Why does it read as perspex?

Three things: the glass color deepens toward the middle of each cell, where the perspex would be thickest; each facet reflects drifting clouds, so neighbors take different tones; and the brightest clouds reflect as crisp white glints with hard edges, as on polished acrylic.

How do I change the size of the cells?

density sets how many cells fill the view, from 0.5 (a few large ones) to 2 (many small ones). The web keeps the same width relative to the cells.

What do edges and the colors do?

edges sets how dark the lines are in the creases between facets and at the foot of the web, from 0 to 1. glassColor sets the cells and latticeColor the web, both any CSS color; the highlights always run toward white.

How do I make it faster or slower?

speed scales all of it: the cells reshaping, the clouds drifting and the wall turning. spin sets only how fast the wall turns, from 0 (still) to 2. paused holds the frame, and time draws one fixed moment, in seconds.

Will it slow my page down?

It costs about 1.6 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 at half resolution, a quarter of the pixels, but the creases soften.

Credits

Inspired by Perspex Web Lattice (opens in a new tab) by Shane on Shadertoy. This is a new shader written from a description of the look, not a port: the height profile, the lighting, the cloud reflections, the palette and the code are ours, and it isn't raymarched. The exact Voronoi border distance follows Inigo Quilez's Voronoi edges (opens in a new tab) article, under the MIT licence, and the hash is lowbias32 by Chris Wellons, in the public domain; both keep their credit lines in the WGSL.

API Reference

PerspexWebLatticeShader

data-slot="perspex-web-lattice-shader"

PropType
density?number

How many cells fill the view, from 0.5 (a few large ones) to 2 (many small ones). @default 1.

Default
1
edges?number

How dark the creases between facets are, from 0 (none) to 1. @default 1.

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

glassColor?string

The perspex cells; their highlights run toward white. Any CSS colour. @default "#e0245e".

Default
"#e0245e"
interactive?boolean

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

latticeColor?string

The web that holds the cells. Any CSS colour. @default "#2b3445".

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

spin?number

How fast the wall turns, from 0 (still) to 2. @default 1.

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