Constant downward acceleration applied to velocity.y.
Exponential velocity damping: v -= v * coefficient * dt each step.
Constant directional acceleration along direction scaled by strength.
Time-rotating XZ acceleration; speed sets rotation rate, strength the magnitude.
Solid-body rotation in XZ around the emitter — tangential force grows with radius.
Divergence-free turbulent flow (FBM curl noise). scale is spatial frequency,
timeScale drifts the field, octaves (≥1) sums frequencies for richer detail.
Plain vector noise — cheaper than curl_noise but not divergence-free (particles may collect into sinks). Useful for jitter/chaos.
Directional force whose strength is modulated by low-frequency noise, giving gusts.
turbulence in [0,1] mixes constant vs. noise-modulated strength.
Pulls particles toward position with linear falloff to zero at radius.
space chooses between absolute world coords and an offset from the emitter origin.
Pushes particles outward from center (negative strength = pull inward).
space chooses between absolute world coords and an offset from the emitter origin.
Clamps |velocity| to max; useful to cap runaway accumulation from forces.
Per-particle stable random size in [min, max] — same slot always gets the same size.
Interpolates particle size from start → end over the particle's lifetime.
Maps |velocity| in [speedMin, speedMax] to size in [sizeMin, sizeMax]; out-of-range clamps.
Constant angular velocity (radians/sec) added to sprite rotation each frame.
Interpolates sprite rotation (radians) from start → end over lifetime.
Interpolates RGBA color from startColor → endColor over lifetime.
Interpolates only the alpha channel over lifetime — cheaper than a full color curve.
While the particle is inside the box [min, max], sets its RGB to the normalized
position within the box (each axis mapped to 0..1). Alpha is preserved.
Particles outside the box are left unchanged. space chooses between absolute
world coords and an offset from the emitter origin.
Sets RGB to normalize(abs(velocity)) — each channel is the fraction of the
velocity's magnitude carried by that axis. Alpha is preserved. Particles with
zero velocity are left unchanged.
Kills particles that leave the axis-aligned bounding box [min, max].
space chooses between absolute world coords and an offset from the emitter origin
(the box is translated as a whole; its axes stay world-aligned).
Half-space collider: a plane through center with the given normal. Particles on the
negative-normal side are pushed back to the surface. If kill is true the particle dies
on contact and bounce/friction are ignored; otherwise bounce in [0,1] is the
coefficient of restitution along the normal and friction in [0,1] damps the tangential
component. space chooses how center is interpreted; normal is always a direction.
Spherical collider: particles inside the sphere are pushed out and reflected.
bounce / friction / kill semantics match plane_collision. space chooses how
center is interpreted.
Optionaldynamic?: booleanWhen true the collider's center + radius are read from a per-frame uniform
(set via ParticlePass.setSphereCollider) instead of being baked in
as constants, so the sphere can move at runtime to track e.g. a physics
body. center / radius here seed the initial value; radius 0 disables it.
Heightmap collider. Three contact modes, checked in order:
kill: true — particle dies on contact (fires on_death + splits).stick: true — particle freezes in place for the rest of its life:
velocity is stored as a unit-length "impact direction" and the
PARTICLE_FLAG_STUCK flag is set. The update loop then skips
movement/force modifiers and the sprite renderer reorients the
quad to face that direction (snow settling on terrain).bounce/friction follow plane_collision semantics.Requires the particle pass to bind a heightmap (handled automatically
when this modifier is present). kill takes precedence over stick.
Pulls particles toward the surface of the bound ParticleGraphConfig.sdf. Uses the
SDF gradient as the attraction direction and the signed distance as a per-step pull
magnitude, so particles converge onto the surface and slow down as they near it.
offset shifts the target surface outward (positive) or inward (negative) in world
units — useful for "halo" hovering or for negative SDF effects.
SDF collider for the bound ParticleGraphConfig.sdf. Particles whose sampled distance
is negative (inside the surface) are pushed back along the gradient with the same
bounce / friction / kill semantics as plane_collision.
Runs an expression graph as a modifier. Accepts either the compact AST
form (ExpressionGraph from particle_expr.ts) or the flat node-graph
form (ExprNodeGraph from particle_expr_graph.ts); the two are
disambiguated structurally ('actions' in graph vs 'nodes' in graph)
and both lower to the same WGSL. Use the AST form when hand-coding for
brevity; use the node-graph form when constructing from the visual
editor or when callers want to preserve the explicit wiring for
visualization. Order in the surrounding modifiers list matters: an
expression placed after gravity sees the updated velocity, etc.
Sticks particles to the bound ParticleGraphConfig.sdf surface on contact. When the
particle's signed distance falls to threshold or below (default 0 = strictly inside),
the particle is snapped onto the surface along the SDF gradient and its velocity is
zeroed. Run after sdf_attractor and integration-affecting modifiers (gravity, drag,
noise) in the modifier list so the snap wins for the frame; with velocity pinned at
zero, the next integration step keeps the position at the surface, so stuck particles
stay stuck for the rest of their lifetime. Other modifiers (color/size over lifetime)
continue to apply. Use a small positive threshold (e.g. 0.005) to grab fast-moving
particles that would otherwise tunnel past the surface in a single step.
Per-frame behavior applied to live particles in the update compute pass.