Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Free

Orb Grid Shader for React

Three soft orbs drifting under a fine grid that bends toward them and takes their colour, drawn on the GPU. For React with Base UI, when a product launch, an AI or a startup hero wants clean structure and colour behind its copy.

Framework

Orb Grid Shader draws three soft orbs, indigo, pink and amber, drifting slowly under a fine square grid. Each orb is also a gravity well: the grid pinches toward it like a spacetime diagram, and its lines and crossings take the orb's colour where it passes beneath, fading to a faint hairline everywhere else. A WGSL fragment shader on WebGPU computes every pixel each frame, so the lines stay one CSS pixel wide and sharp at any size, for about a tenth of a millisecond of GPU time a frame.

Use it behind a product launch, an AI or startup hero, a pricing section or a changelog header. For a flat grid in plain CSS, use the Grid Pattern background; for colour without structure, Mesh Gradient or Gradient Mesh Shader; for a sheet of dots instead of lines, Breathing Dots Shader. It runs only while it's on screen, holds a still frame with reduced motion, and shows a CSS grid over the same glows in browsers without WebGPU.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Usage

import { OrbGridShader } from "@/components/ui/orb-grid-shader";
<OrbGridShader className="flex min-h-[32rem] items-center justify-center">
  <h1 className="text-5xl font-semibold text-white">Launch week</h1>
</OrbGridShader>

Orb Grid Shader is a container: your content sits on top of the grid, 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 page is near-black in both color schemes, so put white or light text over it.

The colors take any CSS color and follow a theme switch, so orbAColor="var(--primary)" ties the first orb to your brand. lineColor sets the hairlines and their highlights, and backgroundColor the page behind them.

Demos

Flat grid

warp={0} turns the wells off, so the grid stays square and only the colour moves: calmer behind a pricing table or dense copy. cellSize={48} opens the grid up, and intensity={0.7} dims the orbs.

Preview width

Interactive, in a card

With interactive, the pointer is one more small well: the grid dimples toward it and its lines brighten as it moves over the card. The well eases after the pointer, and fades out when the pointer leaves, so the grid relaxes. Touch doesn't move it. Here the orbs are cyan, indigo and violet.

Preview width

Performance

About 0.13 ms of GPU time a frame at 1280×720 on an M2 Max laptop; slower GPUs take several times longer. It's one of the lightest shaders, so it keeps quality={1}: the hairlines would blur 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 grid over three soft glows of the same 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 grid 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 orbs drift over tens of seconds. At the default colors, white text stays above 8:1 on 95% of the box and above 12:1 on three quarters of it; only the brightest crossings at the heart of each orb come close to white, so keep small body text off the orbs' centres, especially at intensity above 1.

FAQ

Can I use my brand colors?

Yes. orbAColor, orbBColor and orbCColor set the three orbs, lineColor the hairlines and backgroundColor the page. Each takes any CSS color and follows a theme switch, so orbAColor="var(--primary)" works. Keep the background dark: the orbs add light to it, and on a light page they'd wash out.

How does the grid bend?

Each orb is a gravity well. Near it, the shader reads the grid from a little further out, so the lines bunch together and curve toward the orb, as in a spacetime diagram. warp sets how hard the wells pull, from 0 (a flat grid) to 2; one well alone never folds the grid over itself.

Does cellSize stay the same on a retina screen?

Yes. cellSize is in CSS pixels, from 16 to 96, and the lines are one CSS pixel wide, at any screen ratio and any quality. Lines are anti-aliased, so they stay crisp as they bend.

Can the grid react to the pointer?

Set interactive and the pointer becomes a small fourth well that dimples the grid and brightens the lines under it. It eases after the pointer, fades in as it comes over the box and fades out when it leaves; touch is ignored, so scrolling on a phone never drags it. Without interactive there's no fourth well.

Will it slow my page down?

It costs about 0.13 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. Keep quality at 1: the shader is cheap, and the hairlines need full resolution to stay sharp.

What happens in browsers without WebGPU?

The box keeps its CSS still: a faint grid over three soft glows in the orbs' colors, on the same near-black. Nothing errors: the root's data-state reads unsupported.

API Reference

OrbGridShader

data-slot="orb-grid-shader"

PropType
backgroundColor?string

The page behind the orbs and the grid. Any CSS colour. @default "#09090b".

Default
"#09090b"
cellSize?number

The grid's square cells, in CSS pixels, from 16 to 96. @default 32.

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

intensity?number

How bright the orbs glow, from 0 (a bare grid) to 2. @default 1.

Default
1
interactive?boolean

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

Default
false
lineColor?string

The grid's hairlines away from the orbs, and their highlights. Any CSS colour. @default "#ffffff".

Default
"#ffffff"
orbAColor?string

The first orb, which starts upper left. Any CSS colour. @default "#6366f1".

Default
"#6366f1"
orbBColor?string

The second orb, which starts right of centre. Any CSS colour. @default "#ec4899".

Default
"#ec4899"
orbCColor?string

The third orb, which starts at the bottom. Any CSS colour. @default "#f59e0b".

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

warp?number

How hard the orbs pull the grid in, from 0 (flat) to 2. @default 1.

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