# Physics actions and DOM targets

`SwarmSurface` owns a particle pool mapped to measured DOM targets. Refs, not guessed page coordinates, define where a shape or solid element lives.

```tsx
"use client";
import { useMemo, useRef } from "react";
import { SwarmSurface, swarmActions, type SwarmSurfaceHandle,
  type SwarmTarget, type SwarmObstacle } from "dots-swarm";

export function RainAndReform({ onContinue }: { onContinue: () => void }) {
  const surface = useRef<SwarmSurfaceHandle>(null);
  const halo = useRef<HTMLDivElement>(null);
  const headline = useRef<HTMLHeadingElement>(null);
  const button = useRef<HTMLButtonElement>(null);
  const targets = useMemo<SwarmTarget[]>(() => [
    { id: "halo", kind: "shape", shape: "halo", ref: halo },
  ], []);
  const colliders = useMemo<SwarmObstacle[]>(() => [
    { ref: headline, kind: "text" },
  ], []);
  return <>
    <SwarmSurface ref={surface} targets={targets} colliders={colliders}
      count={1000} color="#eeeeee" style={{ position: "relative", height: 420 }}>
      <div ref={halo} style={{ height: 220 }} />
      <h2 ref={headline}>Make an impact.</h2>
      <button ref={button} onClick={onContinue}>Continue</button>
    </SwarmSurface>
    <button onClick={() => surface.current?.act(
      swarmActions.rain({ gravity: 740, restitution: 0.76 })
    )}>Let it rain</button>
    <button onClick={() => surface.current?.act(swarmActions.reform({
      duration: 1.8,
      targets: [{ id: "continue", kind: "element", ref: button, radius: 16 }],
    }))}>Become the button</button>
  </>;
}
```

- `explode({ anticipation, strength, duration, ... })`: tension first, then scatter. `anticipation` and `duration` are seconds. Use `onActionPhaseChange`/`onActionComplete` for coordinated UI state.
- `rain({ gravity, wind, restitution, duration })`: release under gravity.
- `reform({ duration, targets? })`: a separate gathering action; omit targets to use the current ones, or supply new refs/geometry to become something else.
- An action completing does not implicitly trigger another one. If requested, explicitly reform from the explode completion callback.
- Target kinds include `shape`, `points`, `element`, and `avatar`. `appearance: "solid"` for elements/avatars produces continuous surfaces; a plain core avatar supports expressions without the configurable Pro silhouette renderer.
- Colliders can follow refs or selectors; use narrowly scoped elements from the requested scene. `kind: "text"` uses the text geometry. `viewport` makes the surface viewport-wide and should be deliberate.
- If layout changes after fonts/content load, `surface.current?.refresh()` remeasures it. Do not keep throwing away and recreating the pool.

The solid UI still needs its real DOM click/keyboard handlers and styles. Shape-only targets do not create semantic controls. Position targets relative to the surface, keep them measurable, and account for container overflow when using bursts.

For additional force fields, particle-to-particle collisions, constraints, and custom colliders, use `ProSwarmPhysics` through a stable `createPhysics` callback. Read [pro.md](pro.md).
