Product media placeholder
Replace this area with a screenshot or short walkthrough video during the media sweep.
Runtime-fanout compound — one node = N physicsBody instances + N DOM transform writes. Resolves N elements at bind time from a plain CSS selector and creates one body per element with shared params. Per-element radius via `shape.radiusFromCSS: "--bd"` reads each element's CSS variable (same convention `staggerAnimate` uses). Saves ~3N nodes for ball-drop / scatter patterns. For per-element heterogeneity beyond size (different bodyKind / restitution per element), drop down to primitive `physicsBody` + `domPoseWrite` pairs.
Inputs
| Port | Type | Description |
|---|---|---|
world | any | Wire to the sibling `physicsWorld.world` output. Loader resolves at bind and registers all N bodies in this world. |
enabled | float | When 0, all N bodies removed from broadphase (frozen at last pose). |
progress | float | Optional 0..1 driver. When wired AND `resetBelowProgress` param is set, every body re-snaps to its initial pose + zero velocity on the falling edge of progress crossing the threshold (i.e. when scrolling BACK up past the trigger). For the dental ball-drop pattern: pair with `physicsWorld.pauseBelowProgress` set to the same threshold. |
targetCenterX | float | IK-style target. When wired, overrides authored `initialX` so balls spawn centered on the wired source — typically `physicsStaticBody.centerX`. Eliminates the "two-vh-values out of sync" failure mode where the cup is at one viewport position and balls spawn at another. |
targetTopY | float | IK-style target. When wired, overrides authored `initialY`. Spawn Y = targetTopY + targetTopOffset, so a small negative offset (e.g. -50px) drops balls in from just above the cup mouth. |
targetTopOffset | float | Pixel offset added to `targetTopY` to compute spawn Y. Negative = above the cup mouth (default `-50`). Use to tune drop height without recomputing the cup geometry. |
targetMouthWidth | float | Width of the target's mouth — wire from `physicsStaticBody.mouthWidth`. Drives `spawnPattern: "fanFromMouth"` to evenly distribute N balls across the mouth without hand-tuning per-ball spacing. |
Outputs
| Port | Type | Description |
|---|---|---|
bodyIds | floatArray | Per-element engine handle in match order. Wire to v2 `physicsApplyImpulse[index]` / `physicsCollisionPulse[index]` to address one specific ball inside the stagger. |
count | float | Number of elements matched + bodies created. Useful for downstream `expression` / `arrayPick` consumers. |
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
selector | elementSelector | "" | CSS selector matching N sibling elements (e.g. `".wu-ball"`). One body created per match in document order. |
shape | physicsShape | {"kind":"circle","radius":25} | Shared shape across all N bodies. For circles, set `radiusFromCSS: "--bd"` to read each element's --bd CSS var as the diameter (halved internally for radius). Same per-element convention `staggerAnimate` uses. |
initialX | string | 0 | Spawn X position (CSS units like "20vw" supported). Combine with `initialXSpacing` for evenly spaced fan-out. |
initialY | string | 0 | Spawn Y position. Negative values like "-25vh" spawn balls above the viewport. |
initialXSpacing | float | 0 | Adds `index * spacingPx` to each body's initial X. 0 = all at same X (jitter handles separation if non-zero). step 10 |
initialYSpacing | float | 0 | Y Spacing per Index (px) step 10 |
jitterX | float | 0 | Per-index deterministic hash → ±jitterX px on initial X. Same hash on rebind so balls land at the same spots. min 0; step 10 |
jitterY | float | 0 | Per-index deterministic hash → ±jitterY px on initial Y. Same hash on rebind so balls land at the same spots. min 0; step 10 |
density | float | 1 | Mass per unit area, applied to every ball. Lower = lighter (more bouncy / easier to push); higher = heavier (more momentum). min 0.001; step 0.1 |
restitution | float | 0.5 | 0 = no bounce, 1 = perfectly elastic, > 1 = energy-amplifying. Applied to every ball. min 0; max 2; step 0.05 |
friction | float | 0.5 | Surface friction coefficient on every ball. 0 = ice (slides forever), 1 = typical solid. min 0; max 2; step 0.05 |
linearDamping | float | 0 | Per-second exponential damping on linear velocity for every ball. Higher = settles faster. min 0; step 0.05 |
angularDamping | float | 0 | Per-second exponential damping on angular velocity for every ball. min 0; step 0.05 |
lockRotation | bool | false | When true, balls cannot rotate (locks angular DOF). Useful when ball labels need to stay upright. |
ccd | bool | false | Continuous Collision Detection on every ball. Enables tunnelling-prevention for fast falls / small balls. Costs slightly more CPU. |
writeTransform | enum | true | How the node writes per-frame body pose to each element. `Transform` (default) sets `style.transform = translate(x,y) rotate(r)` directly — fast, simple, but overwrites any inherited transform. Use `CSS Var` when bodies live INSIDE a transformed parent (carousel slot, scroll-pinned stage, FLIP layout) — author-CSS composes via `transform: translate(var(--phys-x), var(--phys-y)) rotate(var(--phys-r))` so the parent transform stays intact. `None` writes nothing; consume `bodyIds` and route to a custom DOM write chain. Legacy boolean values (`true` / `false`) are still accepted in JSON. options transform, cssVar, none |
resetBelowProgress | float | null | Falling-edge reset gate. When set (e.g. 0.75), every body re-snaps to its initial pose + zero velocity the frame `progress` drops below this threshold — fires once on scroll-up past the trigger, then the world stays paused (pair with physicsWorld.pauseBelowProgress at the same value). Leave blank to never reset; balls remain wherever they last fell. min 0; max 1; step 0.05 |
spawnPattern | enum | "explicit" | Selects how the per-element X offset is computed. "explicit" uses the legacy `i * initialXSpacing` math. "fanFromMouth" reads the wired `targetMouthWidth` and the live element count, fanning N balls evenly across the mouth — `initialXSpacing` is ignored. Falls back to "explicit" when `targetMouthWidth` is unwired. options explicit, fanFromMouth |
Use cases
- Dental ball-drop — one stagger node creates 6 dynamic circles from `.wu-ball` siblings, each with its own radius via `--bd`. Replaces 6 physicsBody + 6 domPoseWrite nodes (12 → 1).
- Scatter / confetti — N badges fall under gravity into a target area. Authors set spawn jitter via `jitterX` / `jitterY` for organic randomness (deterministic per-index hash, no Math.random).
- Marble run — N marbles stream from above, hit static path colliders, settle. Stagger handles all N marbles uniformly; per-marble params come from CSS vars.
Related nodes
Envelope
Use physicsBodyStagger as the node type inside a graph node envelope. Add id, optional params, optional connections, and optional activeWhen based on the guide context.
Generated source
Registry faster-motion-docs/node-registry.jsonCategory page /help/faster-motion/faster-motion-node-category-solvers/