Flow Field
Add streaks drifting on a smooth current that the pointer stirs into a vortex.
import { FlowField } from '@youj/motion/backgrounds'
Installation & usage
npm i @youj/motionFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use it where the idea is movement: traffic, data in flight, networks, anything that should feel alive and directed. It reads as weather over the page, and it answers the pointer without asking anything of it. Don't use it behind reading-length text, since the streaks cross everything, and keep it to one animated background per page.
The field fills its nearest positioned ancestor and paints behind your content, so make the container relative and isolate.
import { FlowField } from '@youj/motion/backgrounds'
export function CurrentHero() {
return (
<section className="relative isolate min-h-[560px]">
<FlowField />
<p className="relative text-5xl font-semibold">Watch requests find their way</p>
</section>
)
}Each streak rides a smooth noise current, so neighbors flow the same way and the direction changes slowly across the canvas and over time. A streak lives for a few seconds: it fades in, drifts, fades out and starts again somewhere else. The number of streaks follows the area: at density={1}, one per 3,200 square pixels, capped at 800. A phone and a wide display look equally full.
Description
A hero section over the current; move the pointer across it and the streaks swirl around it and light up.
colors takes two CSS colors: the streaks, then the highlight for streaks the pointer has caught. The defaults are --color-fg-muted and --color-accent. Token references follow the theme, and the field rereads them when the theme changes.
Controls for the size of the swirls, the length of the trails and the pointer.
scalesets the size of the swirls in the current. Above 1 the streaks run in broad, calm sweeps; below 1 the current is busier. It defaults to 1.trail, from 0.3 to 1.5, multiplies the trail length. It defaults to 1.densitysets how many streaks there are per area,intensity(0 to 1.5) how strongly they show, andspeedhow fast the current runs.
The pointer is a vortex: the current curls around it and the streaks it catches turn to the highlight. The pointer is tracked on the window, so the field responds under the content above it. Set interactive={false} where the field sits behind something people work in.
A sparse, slow current behind a status card, with no pointer response.
At a low density, intensity and speed, and a broad scale, the current reads as flow rather than as an effect, a quiet signal that things are moving.
Code
flow-field-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propscolors, intensity, speed… (8)
Props
| Prop | Type | |
|---|---|---|
colors | [streaks: string, highlight: string][streaks, highlight] CSS colors. Default: | – |
intensity | numberOpacity of the streaks, 0–1.5. default 1 | 1 |
speed | numberCurrent speed multiplier. default 1 | 1 |
density | numberStreaks per area, relative. 1 ≈ one streak per 3 200 CSS px². default 1 | 1 |
trail | numberTrail length multiplier, 0.3–1.5. default 1 | 1 |
scale | numberSize of the swirls in the current: above 1 is broader and calmer, below 1 is busier. default 1 | 1 |
interactive | booleanPointer reactivity: the pointer stirs a vortex in the current. default true | true |
className | string | – |
PerformanceIt's drawn with Canvas 2D, not WebGL
Performance
It's drawn with Canvas 2D, not WebGL. Each frame redraws every trail from a short history, in a handful of strokes batched by opacity, so nothing is left behind on the canvas and a resize or a theme change is exact. The canvas renders at a device-pixel ratio capped at 2, and the low motion budget keeps 60% of the streaks. The loop pauses when the field is offscreen or the tab is hidden, and it stops and cleans up on unmount. If the browser can't create a 2D context, it falls back to a static CSS wash of the highlight color.
MotionCurrent, vortex, streak life · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Currenta smooth noise field that shifts slowly over time | no token speed ÷ time scale | speed ÷ time scale | |
| Vortexthe current curls around the pointer within reach | no token per-frame physics | per-frame physics | |
| Pointer strengtheases in and out with the pointer | no token smoothed per frame | smoothed per frame | |
| Streak lifefades in, drifts, fades out and respawns | no token 4 to 10 seconds | 4 to 10 seconds |
Reduced motion
Draws one fixed, seeded field with its trails laid down: the same composition every time. There's no drift and no pointer response, and it redraws only when the size, the props or the theme change.
Reduced motion
The field shows a fixed composition from a seeded layout, so it's the same on every visit and in every screenshot. The streaks and trails keep their colors and density. Only the drift and the pointer response are gone. Switch on Reduced motion in any preview above to compare.
AccessibilityARIA
Accessibility
- It's decorative: the element is
aria-hiddenand ignores pointer events. - Streaks cross whatever sits above them. Keep a quiet pool of canvas behind the copy, as the hero preview does, or lower
intensity, and check your text's contrast there. - The drift is slow and stops offscreen. Under reduced motion the field doesn't move at all.