Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Mobius Sierpinski Shader for React

A Sierpinski carpet of rounded tiles wound into two spiral eyes by a Möbius map, streaming from one eye into the other on charcoal. For React with Base UI, when a generative-art, AI or data hero needs a hypnotic, mathematical pattern behind its copy.

Framework

Mobius Sierpinski Shader draws a Sierpinski carpet, a square split into nine with the middle one kept and the rest split again, and bends it with a Möbius map so the grid winds into log spirals around two eyes. Rounded tiles in periwinkle and warm white stream out of one eye and into the other, each splitting into finer tiles that fade into dust toward the eyes, while the eyes drift slowly. A WGSL fragment shader on WebGPU computes each pixel's place on the grid, so the tiles stay crisp at every size.

Use it behind a generative-art, AI or data hero, or anywhere a mathematical pattern suits the brand. For a single whirlpool in plain CSS, with no GPU, use the Vortex background; for a mirrored, black-and-white pattern, Truchet Kaleidoscope Shader. 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 {
  MobiusSierpinskiShader,
} from "@/components/ui/mobius-sierpinski-shader";
<MobiusSierpinskiShader 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>
</MobiusSierpinskiShader>

Mobius Sierpinski Shader is a container: your content sits on top of the pattern, 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 ground is charcoal in both color schemes, but the tiles are bright, so put white text on a panel such as bg-black/60.

Demos

Amber

accentColor="#f59e0b" turns the large panels amber, depth={6} splits every tile two more times, so the spirals carry finer dust, and wobble={1.6} lets the eyes drift further.

Preview width

In a card

The pattern scales with its box. speed={0.5} halves the flow, which is calmer next to text. The tile edges are the detail here, so the card keeps full quality; the shader is light enough for it.

Preview width

Performance

About 0.3 ms of GPU time a frame at 1280×720 on an M2 Max laptop, at the default depth and at depth={6} alike; slower GPUs take several times longer. Each level of tiles costs a few operations per pixel, and levels whose tiles would be smaller than a few pixels fade out instead of shimmering. At quality={0.5} the tile edges 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, two dark eyes with faint rays and soft patches of the tile colors on charcoal, 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, both eyes in view, and stops. Nothing flashes: the tiles stream steadily and the eyes drift over tens of seconds. Text needs a panel: across the renders we measured, white text reaches 4.5:1 on 82% of the shader and black text on 18%, because the bright tiles are everywhere. White text on a bg-black/60 panel stayed above 5.7:1 on every pixel.

FAQ

What is a Möbius map?

A function of the plane of the form (az + b) / (cz + d), treating each point as a complex number. It maps circles to circles and keeps angles, so small squares stay square. Here it sends one eye to zero and the other to infinity; taking its logarithm turns the circles round each eye into straight lines, which the shader tilts into a grid that winds into log spirals.

How is the carpet drawn?

Each grid cell draws a rounded square in its middle ninth, then splits into a 3×3 grid of smaller cells that do the same, depth times. Each tile picks its color and whether it shows from a hash of its place, and every other spiral arm is denser, so the arms read.

Can I change the colors?

Yes: accentColor sets the large panels and about a quarter of the small tiles, and tileColor the rest. Both take any CSS color, including theme variables such as var(--primary), and follow a theme switch. The charcoal ground stays.

Why do the edges stay crisp, even near the eyes?

The shader knows how large one screen pixel is on the grid at every point, from the map's derivative, so each tile's edge is antialiased over exactly one pixel. Where a level's tiles would be smaller than a few pixels, that level fades out, so the eyes dissolve into dust instead of flickering.

How do I make it faster or slower?

speed scales time: 2 is twice as fast, 0.5 half as fast, and 0 holds the current frame. wobble sets only how far the eyes drift, from 0 (fixed) to 2. paused holds the 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, even at depth={6}, and the CPU only sets a few numbers each frame. It stops when it's off screen. On a slow GPU or a large screen, quality={0.5} renders at half resolution, a quarter of the pixels, but the tile edges soften.

Credits

Inspired by Mobius Sierpinski (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 spiral lattice, the tile shading, the arm pattern, the palette and the code are ours. The rounded box distance is by Inigo Quilez, 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

MobiusSierpinskiShader

data-slot="mobius-sierpinski-shader"

PropType
accentColor?string

The large panels and about a quarter of the small tiles. Any CSS colour. @default "#7b8cff".

Default
"#7b8cff"
brightness?number

Overall brightness of the tiles, from 0.5 to 1.5. @default 1.

Default
1
depth?number

How many times each tile splits into nine, from 1 to 6: more is finer dust toward the eyes. @default 4.

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

tileColor?string

The rest of the tiles. Any CSS colour. @default "#f2eee6".

Default
"#f2eee6"
time?number

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

wobble?number

How far the two eyes drift, from 0 (fixed) 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