shadcn.io is not affiliated with official shadcn/ui
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.
Installation
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.
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.
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"
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.
Related
- Pro


Extruded Mobius Spiral Shader
Tunnels & 3DThe same Möbius spirals, as 3D blocks
- Pro


Truchet Kaleidoscope Shader
Patterns & tilingsA mirrored, black-and-white pattern
- Pro


Another fractal, glazed like ceramics
- Pro


Poincaré Disc Animation Shader
GeometryTiles shrinking forever toward a rim
- Pro


Binary Shader
Digital & retroLiving code for an AI or data hero
- Pro


Quasicrystal Shader
GeometryOrder that never repeats, in color