Skip to content

Components / Backgrounds

Wave Field

Draws a water surface in halftone. It runs the wave equation on the GPU (a height field and its previous step), so a drop spreads into rings that cross, interfere and settle, and the edges absorb them. The cursor drops ripples along its path, a wake of rings behind it; while it rests, a light rain falls around the focus. The steepness of the surface sizes a dot per cell, in two tones on the page's ground; `dots` 0 draws a smooth surface. Every constant is a custom property re-read when the element's style changes. Without WebGL2 or a colour-renderable float texture, or without JavaScript, still rings stand in. Fills its positioned parent.

Live demo

Ripples.

move your cursor

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

Measured

JavaScript with its libs

10.8 kB raw

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

CSS 5.3 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), still rings around 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/wave-field
src/pages/index.astro
<section class="relative min-h-[70vh]">
  <WaveField color="var(--primary)" />
  <h1 class="relative">Ripples on a halftone surface</h1>
</section>

Without a key the registry answers 401 with an upgrade message. Related free primitives: ripple.

Props

PropTypeDefaultDescription
colorstring'oklch(0.62 0.19 262)'Dot colour; any CSS colour, theme tokens included. Re-read when the theme changes.
corestringcolor lifted 50 % toward whiteThe second tone: the colour on the steepest wave fronts.
dotsnumber6Halftone cell in CSS px; 0 draws a smooth surface.
speednumber1Wave speed: 60 simulation steps a second at 1. Fixed steps, so the rings travel at the same pace at any frame rate.
settlenumber0.65How fast ripples die out, per second. 0.3 lets them cross the box; 1.5 keeps them local.
dropnumber1How deep the pointer drops into the surface; 0 turns the pointer off.
rainnumber1.1Ambient drops per second while the pointer has rested for 1.5 s; 0 is a still pool.
focusstring'0.7 0.55'Where the rain falls and the fallback rings sit, as fractions of the box (x y, from the top-left).

Reduced motion

No loop and no rain: three drops settle for a second of simulation and are 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: Water ripple effect (jQuery ripples) · Ripple shader backgrounds. See the migration table.

Ripple

Backgrounds

Rings expand from a point and fade, one after another, like a pulse.

every browseralways

Draws slow noise waves on the GPU, ordered-dithered into pixel cells of one colour on the page's ground, for the look of a 1-bit screen.

every browseralwayspointer

Draws smoke that follows the cursor.

every browserpointeralways

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