shadcn.io is not affiliated with official shadcn/ui
Shaders
FreeStreamlines Shader for React
A wind tunnel drawn on the GPU: hairline streamlines part around invisible cylinders and close behind them, warming to an accent where the flow runs fastest. For React with Base UI, when an infrastructure, AI or engineering page needs a calm, precise backdrop that reacts to the cursor.
Streamlines Shader draws a wind tunnel on the GPU. Sixty hairlines run left to right like smoke, part smoothly around two invisible cylinders that drift slowly through the frame, and close again behind them, wavering a little in their wake. The lines are the contours of a potential-flow stream function: a steady stream plus a doublet for each cylinder, a slowly swinging circulation that tilts the flow round them, and a faint ripple of noise downstream. Where the flow runs fastest, over each cylinder's shoulders, the ink warms to an orange accent. Every pixel is computed each frame by a WGSL fragment shader on WebGPU, for about 0.3 ms of GPU time a frame.
Use it behind an infrastructure, AI or aerodynamics hero, or anywhere a page wants precise, technical line art instead of color. With interactive, the pointer becomes one more cylinder and the lines part around the cursor. For closed contour lines of noise instead of flow, use Noise Contours Shader or the CSS Topography background; for streaks blowing past, the Wind background; for a single rising thread of smoke, Incense Shader. It runs only while it's on screen, holds a still frame with reduced motion, and shows faint CSS lines on the same paper in browsers without WebGPU.
Installation
Usage
import { StreamlinesShader } from "@/components/ui/streamlines-shader";<StreamlinesShader
className="flex min-h-[32rem] items-center justify-center"
interactive
>
<h1 className="text-5xl font-semibold text-neutral-900">Built for scale</h1>
</StreamlinesShader>Streamlines Shader is a container: your content sits on top of the lines, 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 paper is light in both color schemes unless you set backgroundColor, so put dark text on it. With interactive, the pointer's cylinder grows in while the pointer is over the box and shrinks away when it leaves, so at rest the flow is the same as without it.
Demos
Interactive
interactive makes the pointer a cylinder the lines part around; it grows in under the pointer and shrinks away when the pointer leaves. obstacles={1} keeps a single drifting cylinder, so the cursor's stands out, and lines={80} draws the flow finer. Touch doesn't move it.
Dark, in a card
lineColor, backgroundColor and accentColor take any CSS color: here light lines on near-black paper, warming to cyan. lines={40} spaces them out for a small surface.
Performance
About 0.3 ms of GPU time a frame at 1280×720 on an M2 Max laptop, the same with every obstacle on, since each pixel always weighs all four cylinders and the pointer; slower GPUs take several times longer. The lines are 1 CSS pixel wide, so keep quality at 1: half resolution would blur them. 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 faint horizontal CSS lines in your lineColor on your backgroundColor, 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 lines are decoration: they're hidden from assistive tech and let every click through to your content, even with interactive, which only reads the pointer's position. With reduced motion it draws one still frame, the lines parting round both cylinders, and stops. Nothing flashes: the cylinders drift over tens of seconds and the wake wavers slowly. The paper alone gives dark text about 19:1, but the lines run through your text and drop it to 1.4:1 where they cross, so set body text on a panel: black text on bg-white/70 stays above 11:1.
FAQ
How are the lines drawn?
They're the contour lines of a stream function, the textbook way to describe a smooth, steady flow: along each contour the flow runs parallel to the line. A uniform stream gives straight lines; each cylinder adds a doublet, which bends them round a circle; a small circulation tilts the flow; and a faint noise ripple wavers the wake. Each line is antialiased from the function's screen-space derivative, so it stays one pixel sharp at any size.
How do I make the lines part around the cursor?
Set interactive. The pointer becomes a cylinder, eased so it glides; it grows in as the pointer comes over the box and shrinks away when it leaves. Add obstacles={0} to make the cursor the only obstacle, with straight lines at rest. Touch is ignored, so on phones the lines stay as they are.
Can I change the colors?
Yes: lineColor, backgroundColor and accentColor take any CSS color, var(--foreground) included, and follow a theme switch. The accent shows where the flow is fastest, over the cylinders' shoulders; set it to the line color to turn it off. For a dark scheme, swap the line and paper, as in the dark demo.
How do I change how many lines there are?
lines is how many cross the height of the box, from 20 to 140, and thickness their width in CSS pixels, from 0.5 to 2. intensity sets how strongly they show, from 0 (hidden) to 2 (bold). Where lines crowd closer than about 3 pixels, over a cylinder's shoulders on a small box, they blend into a soft tone instead of shimmering.
API Reference
StreamlinesShader
data-slot="streamlines-shader"
accentColor?string
Where the flow runs fastest, over the shoulders of each obstacle, the lines warm to this. Any CSS colour. @default "#ff5a36".
- Default
"#ff5a36"
backgroundColor?string
The paper behind the lines. Any CSS colour. @default "#f5f4f0".
- Default
"#f5f4f0"
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 strongly the lines show, from 0 (hidden) to 2 (bold). @default 1.
- Default
1
interactive?boolean
Whether u.pointer follows the pointer. @default false.
- Default
false
lineColor?string
The lines' colour. Any CSS colour. @default "#1d1d1f".
- Default
"#1d1d1f"
lines?number
How many streamlines cross the height of the box, from 20 to 140. @default 60.
- Default
60
obstacles?number
How many invisible cylinders the flow parts around, from 0 to 4. @default 2.
- Default
2
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.
thickness?number
The lines' width in CSS pixels, from 0.5 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.
Related
- Pro


Incense Shader
Ink, smoke & fireA single rising thread of smoke
- Pro


Noise Contours Shader
Patterns & tilingsClosed contour lines of noise instead of flow


Ridgeline Shader
Digital & retroMonochrome line art as a stacked plot, free
- Pro


Moire Shader
GeometryFine lines beating into soft fringes
- Pro


Guilloche Shader
GeometryHairlines woven into an engraved rosette


Orb Grid Shader
GeometryA hairline grid that bends round orbs, free