Skip to content
WANCRAFT

E.140Backgrounds & Effects

Metaballs

Lava-lamp blobs that drift, merge and split through an implicit field — your pointer joins in as one more.

NewReactCanvasTailwindadvanced

Overview

Metaballs samples the classic implicit field Σ r²/d² on a coarse grid — never more than a quarter of the pixel count, and coarser on large containers — and draws the surface where the field crosses 1. Fill mode writes a small image whose edge alpha is a signed-distance ramp, (field − threshold) / |∇field|, so the threshold stays smooth when the image is upscaled; colour shifts from the accent at the surface to the secondary colour in the dense cores. Outline and contour modes trace the same field with marching squares into crisp vector lines — one surface, or a topographic stack of iso-lines. Blobs follow deterministic Lissajous paths, the pointer (or a finger while it is down) is an extra ball that eases in and out, and the loop pauses offscreen and in hidden tabs. Reduced motion renders a single still frame.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/metaballs.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/metaballs.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/metaballs.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { Metaballs } from "@/components/ui/metaballs";

export function MetaballsExample() {
  return (
    <div className="relative h-80 w-full overflow-hidden rounded-xl">
      <Metaballs />
    </div>
  );
}

03Props

PropTypeDefaultDescription
countnumber6Number of drifting blobs (2–16).
speednumber0.6Drift speed multiplier; 0 freezes the blobs while the pointer ball keeps working.
sizenumber1Blob size multiplier, relative to the container's area.
mode"fill" | "outline" | "contours""fill"Solid fill with a field-strength gradient, one crisp outline, or six topographic iso-lines.
pointerbooleantrueAdd the pointer (or a finger while touching) to the field as an extra ball.
accentstring"#FF7448"Colour at the surface, where the field is weakest (any CSS colour).
secondarystring"#FFC46B"Colour in the cores, where the field is strongest (any CSS colour).
childrenReactNode—Optional content layered above the field.
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the root, which is absolute inset-0 — place it inside a positioned parent.

04Features

  • 01Implicit field Σ r²/d² sampled on a grid of at most ¼ the pixel count (capped at 64k samples)
  • 02Signed-distance edge ramp, (f − 1) / |∇f|, keeps the upscaled threshold smooth at any grid size
  • 03Marching squares (with saddle disambiguation) for crisp outline and contour modes
  • 04Colour gradient by field strength, from the surface colour to the core colour
  • 05Pointer ball that eases in, follows smoothly and fades out; touch works while the finger is down
  • 06DPR-aware canvas (capped at 2), ResizeObserver, pauses offscreen and in hidden tabs, theme-aware colours
motion · continuousinput · pointerinput · viewport

05Accessibility

  • The canvas is aria-hidden and ignores pointer events; layered content stays fully interactive.
  • prefers-reduced-motion renders one still frame with no drift and no pointer ball.
  • Very light colours are darkened slightly on light themes so the blobs stay visible.

06Limitations

  • The field is recomputed on the CPU every frame; keep the blob count modest on very large, full-screen containers.