ParticleField
An interactive particle ocean with interchangeable formations and custom point clouds.
Move over the field to attract particles. Custom helix uses supplied XYZ positions. Motion follows your device preference until you choose Play.
Installation
Usage
Install with the registry command above in a React app with Tailwind, shadcn and
its cn utility configured. The registry installs [email protected] and
@vgpu/[email protected] and copies the renderer source and MIT attribution. WGSL is
prepared inside the lazy renderer; no Next.js or Vite shader-loader configuration
is required. Import from your own components directory.
import { ParticleField } from "@/components/ParticleField";
<ParticleField shape="ocean" color="#64748b" className="min-h-[620px]">
<div className="px-6 py-40 text-center">
<h1 className="text-5xl">An ocean of possibilities.</h1>
<a href="/explore" className="mt-8 inline-block rounded-lg border px-5 py-3">
Explore
</a>
</div>
</ParticleField>Children remain clickable above the decorative canvas. Pointer coordinates use
the container bounds, including when the cursor is over foreground content.
Choose sphere, torus or ribbon to change the overall formation. The individual
particles remain circular. Give the container a height or sufficient padding.
Custom formations
Pass an object with a packed Float32Array of XYZ positions. The renderer centers
the points and uniformly fits their bounds, preserving their proportions. Model
units do not matter. Keep the object stable; replace it when the shape changes.
Do not mutate its array in place. Custom arrays must contain 1–262144 finite XYZ
triplets. An invalid array reveals the fallback and logs a diagnostic; remount
after correcting it.
"use client";
import { useMemo } from "react";
import { ParticleField } from "@/components/ParticleField";
export function Helix() {
const shape = useMemo(() => {
const positions = new Float32Array(8192 * 3);
for (let i = 0; i < 8192; i++) {
const t = i / 8191;
const angle = t * Math.PI * 10;
positions.set([Math.cos(angle), (t - 0.5) * 4, Math.sin(angle)], i * 3);
}
return { positions };
}, []);
return <ParticleField shape={shape} className="h-[440px]" />;
}SVG, text and mesh sampling can produce the same point format. Their conversion is a recipe-level concern. Custom point clouds retain their supplied formation and share cursor attraction; this API does not invent continuous custom motion, simulate fluid on arbitrary meshes, or morph between unrelated point sets.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
shape | "ocean" | "sphere" | "torus" | "ribbon" | { positions: Float32Array } | "ocean" | Overall particle formation. Custom point objects need a client boundary. |
color | CSS color string | Inherited text color | Particle ink, including alpha. Theme changes redraw a paused field. |
density | number | 1 | Visible fraction, clamped to 0–1. Does not change FFT resolution. |
speed | number | 1 | Ambient time multiplier, clamped to 0–4. Zero freezes time. |
interaction | boolean | true | Enable mouse attraction; touch is ignored. |
strength | number | 1 | Attraction multiplier, clamped to 0–2. |
radius | number | 1.65 | Attraction radius in aspect-correct normalized screen units, clamped to 0.1–3. |
paused | boolean | false | Freeze ambient motion; mouse attraction can still respond. |
respectReducedMotion | boolean | true | Show a still and disable attraction under reduced motion. Override only after explicit opt-in. |
fallback | ReactNode | Empty | Decorative content shown while loading or when WebGPU cannot render. |
children, className, style, other div props | Native div props | — | Foreground content, sizing and container styling. |
Rendering and lifecycle
WebGPU requires a supporting browser and secure context (HTTPS or localhost). The renderer loads on the client. Unsupported hardware, initialization errors or device loss reveal the fallback; foreground content stays available. Remount to retry after device loss. Reduced motion gets a still frame by default.
The ocean uses the MIT-licensed vgpu FFT ocean example at 512² particles, with independently authored pointer interaction and presentation. Other presets use 16384 particles and skip the FFT and bloom pipeline. Custom point clouds use their supplied count. Density hides a stable subset; it does not reduce ocean simulation cost. The frame loop caps at 30 FPS and 1.6 DPR, pauses offscreen and in hidden tabs, and skips GPU submissions when a paused field settles. Multiple ocean instances are expensive; prefer one shared background. Desktop verification does not establish phone performance.
Shape changes and resizes rebuild GPU resources while preserving the animation
phase. Unmount releases listeners, observers, frames and the GPU device.
The ocean has a softer opacity and bottom fade; compact presets are unmasked.
Keep LICENSE.vgpu.txt when reusing or distributing these files. The source
example revision is recorded in PROVENANCE.md in this repository.
Inspired by https://www.agent-native.com/ — steal-ui entry agent-native-hero-particles.