Skip to content

Components / Backgrounds

Fluid Field

Draws smoke that follows the cursor. It is a real fluid simulation on the GPU (advection, vorticity confinement and a pressure solve in WebGL2), drawn in two tones: the ground and one colour. The dye is one channel coloured at display time, so a theme change never resets the smoke; `dots` lights a halftone grid instead of a wash. A slow emitter wanders around the focus while the pointer is still and on touch. Every constant is a custom property re-read when the element's style changes. Without WebGL2 or JavaScript a still glow stands in. Fills its positioned parent.

Live demo

Smoke.

move your cursor

Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.

Measured

JavaScript with its libs

12.8 kB raw

1.9 kB raw of its own, 1.1 kB gzip. The rest is the runtime, the canvas helper, the GL core and the fluid solver, loaded once for every effect on the page.

CSS 5.2 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.

Browser support

Baseline · widely available

Chrome 56 · Firefox 51 · Safari 15; needs webgl2. Elsewhere: without WebGL2 or a colour-renderable float texture (EXT_color_buffer_float), a still radial glow in the colour at the focus.

Install Pro

The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:

terminal
npx shadcn@latest add @moonarc-pro/fluid-field
src/pages/index.astro
<section class="relative min-h-[70vh]">
  <FluidField color="var(--primary)" />
  <h1 class="relative">Move your cursor. The smoke follows.</h1>
</section>

Without a key the registry answers 401 with an upgrade message. Related free primitives: glyph-field, aurora.

Props

PropTypeDefaultDescription
colorstring'oklch(0.66 0.2 292)'Smoke colour; any CSS colour, theme tokens included. Re-read when the theme changes.
corestringcolor lifted 45 % toward whiteThe second tone: the colour where the smoke is densest.
curlnumber22Vorticity confinement: how much the smoke curls. 0 is laminar, 40 is turbulent.
fadenumber0.9How fast the smoke fades, per second. 0.3 lingers; 2 is a quick wisp.
radiusnumber0.22Splat radius as a share of the field.
forcenumber1Pointer force multiplier.
dotsnumber0Halftone cell in CSS px; the dye sizes a dot per cell. 0 draws a smooth wash.
ambientbooleantrueA slow emitter wanders around the focus after two seconds without pointer movement, and on touch.
focusstring'0.7 0.55'Where the ambient emitter wanders and the fallback glow sits, as fractions of the box (x y, from the top-left).

Reduced motion

No loop and no emitter: a few splats around the focus are settled by a second of simulation and drawn once, as a still. The pointer does not stir it.

With ClientRouter

Bound through the shared runtime: the frame loop, observers and the pointer listener are released before the swap and the WebGL context is lost on purpose after it, so the GPU memory goes with the page; five round trips leave one context. Under transition:persist the canvas keeps its context and starts again on the next page.

Why it is built this way

Replaces: SplashCursor (React Bits) · Smoke / fluid hero (React Bits Pro) · WebGL-Fluid-Simulation (PavelDoGreat). See the migration table.

Ambient gradient field: three soft colour fields drifting at three coprime speeds, a slow hue rotation so the colours themselves move, and a film-grain layer stepped at 12 fps.

every browseralways

A field of braille dots that reads as a flowing contour map: two waves cross it, the colour turns along a sunset arc, and the cursor carves a trail that decays one dot at a time.

newly · Chrome 125 · Firefox 128 · Safari 17.2pointeralways

Makes an image turn liquid under the cursor and settle back.

every browserpointer

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown