API reference

The declarative authoring reference

Shipped · support rows generated

Most of Fundamental is authored in markup, not in code: you put a contract on an element and the field reads it. This is that contract, whole — the 36 data-* attributes, the 37 forces, the 9 feedback channels the field writes back, and the vocabularies they draw on. Every entry carries a per-platform support row, rendered from the generated parity matrix — so you can see what a Swift or Kotlin app actually gets before you design around it.

How to read a support row. JS ✓ means the capability exists on that plane — however that plane spells it. The three planes are different idioms, and an idiom difference is an equivalent, not a gap: the matrix collapses data-body, .fieldBody(tokens:) and Modifier.fieldBody(tokens) to one capability before comparing. A Kotlin — is therefore a real gap: the plane has no way to express that capability at that layer today. Nothing on this page is hand-typed; the chips are read out of data/parity-matrix.json, which pnpm check:docs regenerates and diffs on every CI run.

A body, three ways

Same contract, three idioms. The DOM scan finds bodies by attribute; SwiftUI and Compose attach them through a view modifier. Underneath, all three build the same body and run the same force math — the cross-plane conformance golden pins 6 forces (attract, repel, stream, swirl, tether, viscosity) to identical output at depth: 0.

<!-- a body is an ordinary element with a contract on it -->
<article
  data-body="attract swirl"
  data-strength="0.8"
  data-range="240"
  data-spin="1.2"
  data-when="active"
  data-feedback
>
  <h2>Gravity well</h2>
</article>
PlaneHow a body is declaredContract parameters at that layer
JS `data-*` attributes on any element, found by the DOM scan 42 of 42
Swift the SwiftUI `.fieldBody(tokens:…)` view modifier 4 of 42
Kotlin the Compose `Modifier.fieldBody(tokens, …)` modifier 4 of 42

Read that honestly. The DOM scan is the widest declarative surface by a long way — the attribute lane costs nothing to extend, so it carries the whole contract. The SwiftUI and Compose modifiers expose the common core (tokens, strength, range, plus feedback on Swift and spin on Kotlin); everything else on those planes is reached through the programmatic body spec, which is much closer to parity. The gap is in the declarative sugar, not in the engine.

The body contract — every data-* attribute

A body is configured entirely in markup. Each force reads only the attributes it uses and ignores the rest, so you can set a few and compose freely. The support row on each entry is for the declarative layer specifically — see the programmatic spec for what a port can express in code.

The universal contract

Every body reads these, whatever forces it carries. `data-body` is the only required attribute — it turns an element into a body and names the forces it exerts; the rest tune them.

data-body tokens JS ✓ Swift ✓ Kotlin ✓
Space-separated force ids — required to make an element a body. Forces compose.
data-strength number · default 0.5 JS ✓ Swift ✓ Kotlin ✓
Force magnitude S.
data-range px · default 280 JS ✓ Swift ✓ Kotlin ✓
Influence radius d_max.
data-spin number · default 1 JS ✓ Swift — Kotlin ✓
Sign/strength of rotation — swirl, charge, magnetism, lens.
data-angle deg · default 0 JS ✓ Swift — Kotlin —
Heading — stream, jet, gate, shear, align.
data-color hex JS ✓ Swift — Kotlin —
Accent override on engage, and the carried color for pigment.
data-when condition · default '' JS ✓ Swift — Kotlin —
Gate the force on a condition: active, fast, slow, hot, cool, scrolling.
data-feedback flag JS ✓ Swift ✓ Kotlin —
Opt into two-way density write-back (the --d custom property).

Semantic authoring — say what you mean

The intent lane compiles a plain-language purpose into forces, so `data-body` becomes optional. Reach for it when the *meaning* is stable and the physics is an implementation detail.

data-intent intent JS ✓ Swift — Kotlin —
Semantic authoring: a plain-language intent (e.g. "highlight" or "warn") that the engine compiles into the body's forces. When set, data-body is optional — the intent provides a default token + strength.
data-intensity number · default 1 JS ✓ Swift — Kotlin —
Modifier for data-intent: scales the compiled strength (0 = subtle, 1 = default, 2 = strong). Ignored when data-body is set directly.
data-risk 'low' | 'medium' | 'high' JS ✓ Swift — Kotlin —
Intent hint for data-intent compilation — tunes the body's compiled forces when the intent has risk-sensitive variants.
data-field-role role JS ✓ Swift — Kotlin —
Semantic role: source / sink / anchor / boundary / sensor / display. Maps the element to a default body token and feedback (data-body is optional). sensor/display are feedback-only (no force).

Shape, species & authority

Who a body acts on, what geometry it acts from, and who owns its position.

data-shaped flag JS ✓ Swift — Kotlin —
Shaped source — forces reference the nearest point on the element box, so matter shells the shape instead of bunching at its centre.
data-target SVG path selector JS ✓ Swift — Kotlin —
DOM host morph target: a CSS id selector for an inline SVG <path> in the field host root (for example #mark-path). The DOM host samples its geometry in field coordinates; unsupported hosts may omit target resolution.
data-affects species JS ✓ Swift — Kotlin —
Matter tagging — a comma-separated species set this body acts on (e.g. "1" or "1,2"). Matter whose species is outside the set is skipped entirely (no force, no density sample). Omit to act on all matter (the default). Lets pollen / seeds / spores share one field, each pulled only by its own bodies.
data-species number JS ✓ Swift — Kotlin —
The species tag a spawn source stamps on the matter it emits, so a downstream data-affects body can act on it selectively. Particles default to species 0.
data-authority anchored | kinematic | dynamic · default anchored JS ✓ Swift — Kotlin —
Body-authority (substrate doc 04) — who owns the body position. anchored (default) measures from the DOM/host rect; kinematic = the engine writes the visual transform (the data-move pattern); dynamic = the engine owns position/velocity and the body moves — it integrates under the net field each frame (recoil / field-to-body coupling, doc 04 Step 5); momentum-recoil from own emission + torque are later refinements. Reported by query()/snapshot(). Experimental.

Per-force parameters

Each force reads only the attributes it uses and ignores the rest, so these compose freely — a body with no `sink` token simply never reads `data-absorb`.

data-absorb px · default 64 JS ✓ Swift — Kotlin —
Capture radius for the sink force.
data-max number · default 60 JS ✓ Swift — Kotlin —
Load at which a sink supernovas (releases).
data-life frames · default 90 (spawn) JS ✓ Swift — Kotlin —
Source budget: how long each particle a spawn source emits lives. An unbudgeted source gets the safe default 300 (and a dev warning).
data-cap number JS ✓ Swift — Kotlin —
Source budget: the most live particles a spawn source sustains — the emission rate is clamped to cap/life. The unbudgeted-source safe default is 120.
data-budget flag JS ✓ Swift — Kotlin —
Source budget flag — marks a spawn source as explicitly budgeted without specifying data-life or data-cap. Suppresses the unbudgeted-source dev warning.
data-sink flag JS ✓ Swift — Kotlin —
Secondary budget indicator for a spawn source — if present, the engine treats the source as budgeted (equivalent to data-budget). Useful when paired with the sink force.
data-dock flag JS ✓ Swift — Kotlin —
Collapse into a sink when captured; fires field:captured / field:released. Read on the body element; needs data-move.
data-warp flag JS ✓ Swift — Kotlin —
Teleport to a paired body through a warp throat; fires field:relocated. Read on the body element; needs data-move.
data-pair selector JS ✓ Swift — Kotlin —
Warp force: CSS selector for the partner body (the other end of the wormhole). Matter that enters this body is teleported to the partner. Omit on non-warp bodies.
data-twist deg · default 0 JS ✓ Swift — Kotlin —
Warp force: rotation applied to matter crossing the throat — twist the exit angle.
data-scale number · default 1 JS ✓ Swift — Kotlin —
Warp force: scale factor applied to matter velocity when it exits the partner throat.
data-screen-min number · default 0 JS ✓ Swift — Kotlin —
Floor of the screen modifier's attenuation factor (0 = other forces may cancel fully at the screen's core).
data-charge-gated flag JS ✓ Swift — Kotlin —
Opt-in fieldflow mode (magnetized plasma) — the fieldflow force follows only charged matter (charge ≠ 0) so it composes with charge; neutral matter drifts free. Omit for the default, which advects ALL matter (neutral-medium transport).
data-potential channel name · default 'height' JS ✓ Swift — Kotlin —
The addField channel the relief force admits as a scalar POTENTIAL (terrain height, a cost surface) — matter is transported down its gradient, -grad(Phi). Names which channel; omit for "height". data-spin picks the sign: >= 0 (default) drains downhill, < 0 climbs. A pure no-op when no such channel is registered, so a relief body on a field with no host channel does nothing at all.
data-preset name JS ✓ Swift — Kotlin —
Expand a preset into several co-located bodies — blackhole, galaxy, tornado, …

Typographic reaction

The variable-font axes `--d` drives on a feedback body — type that thickens where matter gathers.

data-fmin / data-fmax number JS ✓ Swift — Kotlin —
Variable-font weight range that --d drives on a feedback body.
data-opsz number JS ✓ Swift — Kotlin —
Optical-size axis to drive alongside weight.

Engine-set markers (read, do not author)

These appear in your DOM but are written BY the engine or the platform. They are documented so you can read them in CSS and in the inspector — not so you can set them.

data-active "1" engine-set
Engagement state — set automatically on hover/focus of a [data-hot] element.
data-field-boundary flag (engine-set) JS ✓ Swift — Kotlin —
Field-ownership marker — ENGINE-SET by a contained host (containerHost / the bounds: option) on its bounds element, not authored. A body belongs to the NEAREST enclosing marked boundary: outer/page-field scans skip bodies inside it, the contained field owns exactly its subtree, and nesting resolves to the nearest. Removed on destroy so the outer field re-adopts on rescan.

What an element reads back

The other half of Body Matter Interaction. The attributes above make an element a body — it pushes the field. These make an element a consumer: it reacts to matter arriving, without exerting any force of its own. Every one of them needs data-move, which is how an element opts in to being relocated at all.

data-move
Opt the element in to relocation — the engine may translate or collapse this node. Required by every consumer below.
data-on
Bind a field event to this element (capture, release, proximity), so the DOM reacts to matter reaching it.
data-class
Toggle a class on this element as field state crosses a threshold — the class-toggle channel.
data-emit
Clone a decorative <template> child as the body emits matter.
data-hot
Mark the element a heat source; matter warms as it passes, feeding the thermal channel.
data-formation
Declare a section formation (for example "wells"); after roughly 6s of no input the field drifts back to calm ambient. Also a <field-root> attribute — see FIELD_ROOT_ATTRS.

Platform bindings (@fundamental-engine/dom)

A separate layer from the body contract above. These are read by the DOM package rather than the engine core — a lint contract, two inputs to the grounded-recency metric, and a pair that binds a drawn visual to the body it depicts. Several of these names are also written for you when a recipe generates a binding; what is documented here is the authored side, the contract the engine honours if you write it yourself.

data-field-at read by metrics.ts
Timestamp (ISO or epoch ms) the grounded-recency metric decays from. Without it an element has no age and contributes no recency signal.
data-field-halflife read by metrics.ts
Half-life in ms for the recency decay that `data-field-at` feeds — how fast the signal fades.
data-field-relation read by lint.ts
Declares a relationship from this element; the platform lint pairs it with `data-field-target` and warns when the target resolves to nothing.
data-field-target read by lint.ts
The selector a `data-field-relation` points at. A relation whose target does not resolve is a relation pointing at nothing, which the lint reports.
data-field-visual-for read by visual-bindings.ts
Binds a visual element to the body it depicts, so a drawn overlay tracks the right matter.
data-field-visual-role read by visual-bindings.ts
The bound visual's role — what the visual is FOR, so the binding layer can treat a contour differently from a label.

The same contract without an element

A body does not need a backing element. addBody takes a BodySpec — the same contract, supplied directly, with a rect callback as the position source. This is how a Three.js mesh, a SwiftUI canvas, or a headless host participates. The ports are near-parity here, which is why a Swift or Kotlin app is not limited to the four-parameter modifier above.

absorb-r JS ✓ Swift ✓ Kotlin ✓
angle JS ✓ Swift ✓ Kotlin ✓
Heading in degrees for the directional forces (stream, jet, gate, shear, align). Kotlin spells it angleDeg.
authority JS ✓ Swift — Kotlin —
Who owns the body’s position: anchored (measured from the host rect), kinematic (the engine writes the visual transform), or dynamic (the engine owns position and velocity and the body moves under the net field). JS only today — the ports treat every programmatic body as anchored. Experimental.
color JS ✓ Swift ✓ Kotlin ✓
Tint carried for pigment transport, and the accent override on engage. Kotlin spells it tint.
data JS ✓ Swift ✓ Kotlin ✓
An arbitrary record carried with the body and surfaced on its handle. Opaque to the engine.
feedback JS ✓ Swift ✓ Kotlin —
The per-body feedback callback (onFeedback) — this body’s channels, every frame, demultiplexed from the global sink. The Kotlin BodySpec does not carry one yet; use a FeedbackSink or the StateRegistry on the platform layer there.
identity JS ✓ Swift ✓ Kotlin ✓
First-class identity — a stable id (plus optional namespace / kind / host) so snapshots, diff, replay and relationships can reference the body rather than the handle. Omitted, the engine derives a deterministic body-N.
potential JS ✓ Swift — Kotlin —
range JS ✓ Swift ✓ Kotlin ✓
Radius of influence, in field pixels.
scale JS — Swift — Kotlin ✓
spin JS ✓ Swift ✓ Kotlin ✓
Rotation sign and scale for swirl / lens / charge / magnetism.
strength JS ✓ Swift ✓ Kotlin ✓
Overall force magnitude; scales every token on the body.
tokens JS ✓ Swift ✓ Kotlin ✓
The force ids this body emits — a space-joined string or an array. The only required field besides the position source.
twist JS — Swift — Kotlin ✓

The 37 forces

Compose them on any data-body element, space-separated. Every token is a real, passported engine token and every one of them ships on all three planes — this is the part of the surface with no parity story to tell, and the conformance golden proves the math agrees, not just that the names match. Each card is generated from the engine's own catalog: the per-frame law, the attributes it reads, a real-world use, and its support row.

Runtime tokens only. Each card carries a badge from the engine's classification — either a fundamental-field parent (gravity EM strong weak) or a kind (designed, derived, transport, metric). Only gravity / electromagnetic / strong / weak are conceptual natural fields; charge and magnetism are electromagnetic expressions, while thermal, diffuse, and collide are derived behaviors. Concepts (spring, orbit), metrics (mass) and diagnostics (potential) are not tokens and do not appear here — see the four-field model and the pattern model.

Canonical nine (9)

The bounded, UI-legible verbs — what a body does to nearby matter. Designed runtime tokens, not natural translations.

attract designed live
v += û · (1 − d/r)² · S · 0.5 (+ orbital swirl)

A soft gravity-like well, optionally bent into a spiral.

data-strengthdata-range

A hero word that pulls the field into orbit around it.

JS ✓ Swift ✓ Kotlin ✓
repel designed live
v −= û · (1 − d/r)² · S · 0.5

Soft outward push — carves a void.

data-strengthdata-range

A dismiss control that opens a clean void in the field.

JS ✓ Swift ✓ Kotlin ✓
swirl designed live
v += û⊥ · (1 − d/r)^1.4 · S · 0.45 · spin (+ 0.12 inward)

Tangential swirl with light inward retention.

data-strengthdata-rangedata-spin

A loading state that spins the field into a swirl.

JS ✓ Swift ✓ Kotlin ✓
stream designed live
v += ĥ · (1 − d/r)^1.1 · S · 0.5

A steady directional current along a heading.

data-strengthdata-rangedata-angle

A scroll cue that combs the field downwind like a current.

JS ✓ Swift ✓ Kotlin ✓
viscosity designed
v −= v · (1 − d/r) · (0.05 + 0.07·S)

Viscosity — thickens the medium, bleeding momentum.

data-strengthdata-range

A modal backdrop the field thickens behind — motion settles.

JS ✓ Swift ✓ Kotlin ✓
jet designed
feed: v += û·(1 − d/r)²·(0.25 + 0.15·S); at the nozzle (<24px): relaunch as a jet

A conduit — draws matter in, jets it out along a heading.

data-strengthdata-rangedata-angle

A call-to-action that draws matter in and jets it back out.

JS ✓ Swift ✓ Kotlin ✓
tether designed live
v += û · (d − rest) · k; v *= 0.985, rest = 0.6·r

A tether with a rest length — holds matter at a shell radius.

data-strengthdata-range

A pinned element the field tethers to at a rest radius.

JS ✓ Swift ✓ Kotlin ✓
wall designed
inside box: push out the shallower axis, v_axis ← −0.85·v_axis

An axis-aligned bouncing wall — sparks on hard impact.

box-sized — no params

A card the field bounces off — a wall that sparks on impact.

JS ✓ Swift ✓ Kotlin ✓
sink designed
capture within absorbR; at capacity, release everything (supernova)

Captures matter, holds it (conserved), then releases it.

data-absorbdata-max

A cart that accretes matter, then releases it on checkout (a supernova).

JS ✓ Swift ✓ Kotlin ✓

Natural & derived (8)

Runtime tokens drawn from physics: some express a fundamental field (gravity, charge, magnetism), others are derived behaviors (thermal, diffuse, collide), a transport (propagate), or a metric (memory). The per-card badge tells you which.

gravity gravity live
F = G·M·d̂ / (d² + ε²), ε = r_s = 2GM/c²; |v| ≤ c

True softened inverse-square — a real 1/d² law.

data-strengthdata-range

A heavy headline with a true 1/d² pull — a real well.

JS ✓ Swift ✓ Kotlin ✓
charge EM
F = −σ·q·GM·d̂ / (d² + ε²)

The signed sibling of gravity — like repels, opposite attracts.

data-strengthdata-rangedata-spin

Two states that demix charged matter into +/− domains.

JS ✓ Swift ✓ Kotlin ✓
magnetism EM
v ← rotate(v, θ), θ = q·spin·B·(1 − d/r)

The Lorentz force — curves a moving charge, doing no work.

data-strengthdata-rangedata-spin

A region that curves moving charge into cyclotron arcs.

JS ✓ Swift ✓ Kotlin ✓
thermal derived
v += √(2T) · ξ, T = S·(1 − d/r), ξ ~ N(0,1) per axis

Langevin/Brownian agitation — a real temperature in the medium.

data-strengthdata-range

A "hot" badge that agitates the field with Brownian heat.

JS ✓ Swift ✓ Kotlin ✓
collide derived
overlapping, approaching discs exchange normal momentum (e = S)

Elastic pairwise collision — the hard-sphere billiard force.

data-strengthdata-range

A dense cluster where particles ricochet like billiards.

JS ✓ Swift ✓ Kotlin ✓
diffuse derived
deposit φ; v += ∇φ · S; field blurs ∂φ/∂t = D∇²φ

A pheromone field — deposit a mark and follow the diffused gradient.

data-strengthdata-range

A trail particles lay down and follow — stigmergy.

JS ✓ Swift ✓ Kotlin ✓
propagate EM
pulse φ at the source; v += r̂·|∇φ|·S (ride the front out); ∂²φ/∂t² = c²∇²φ

A travelling wave — particles ride the expanding front.

data-strengthdata-range

A button that fires a real shock wave across the field.

JS ✓ Swift ✓ Kotlin ✓
memory metric
M(x) += λ where matter sits; v += û · (1 − d/r)² · S · 0.5 · (1 + μ·M)

The field remembers — occupancy wears in paths that pull harder.

data-strengthdata-range

A frequently-used path that wears in and starts pulling harder.

JS ✓ Swift ✓ Kotlin ✓

Designed extended (20)

Pure per-particle effects — bends, gates, transitions, modifiers, and strong/weak analogues.

lens designed
v ← rotate(v, θ_max · (1 − d/r) · sign)

Rotates velocity, preserving speed — bends paths without adding energy.

data-strengthdata-rangedata-spin

A link that bends a passing flow into caustics — no capture.

JS ✓ Swift ✓ Kotlin ✓
gate designed
if v·n < 0: v −= 2(v·n)·n

A one-way membrane — passes along its heading, reflects the reverse.

data-angle

A one-way membrane: matter passes through but not back.

JS ✓ Swift ✓ Kotlin ✓
buoyancy designed live
v_y −= (ρ_med − ρ_p)·g, ρ_p = base / (size·(1 + heat))

A constant lift/sink by density — light matter rises, dense settles.

data-strengthdata-range

A feed where light/hot items rise and dense ones settle.

JS ✓ Swift ✓ Kotlin ✓
shear designed
v∥ += S · (offset⊥ / r) · (1 − d/r)

A laminar velocity gradient — flow grows with perpendicular offset.

data-strengthdata-rangedata-angle

A divider with laminar flow — layers slide past each other.

JS ✓ Swift ✓ Kotlin ✓
crystallize strong
if heat < ½: v += (node − p)·k; v *= 0.9

Snaps cool matter onto a lattice; melts and frees it when hot.

data-strengthdata-range

A "cool" section where matter snaps onto a lattice.

JS ✓ Swift ✓ Kotlin ✓
align strong
v += (ĥ·|v| − v)·k, ĥ = neighbour-mean heading (or the body heading)

Steers toward a heading, preserving speed — flock alignment.

data-strengthdata-rangedata-angle

A nav where particles flock to a shared heading.

JS ✓ Swift ✓ Kotlin ✓
wind designed
v += curl(ψ)·S, ψ = sin(x·s + 0.2t)·cos(y·s − 0.2t)

Divergence-free curl-noise turbulence.

data-strengthdata-range

A hero with divergence-free turbulence drifting through it.

JS ✓ Swift ✓ Kotlin ✓
cohesion strong
neighbours: push if d < r₀, pull if r₀ < d < r₁

Short-range pressure + mid-range pull — surface tension.

data-strengthdata-range

A tag group that behaves like a droplet — a swarm with a skin.

JS ✓ Swift ✓ Kotlin ✓
pressure strong
ρ = Σ W(d, h); v += −k·(ρ − ρ₀)·∇W (push down the density gradient)

SPH density relaxation — incompressible even-fill via mutual repulsion.

data-strengthdata-range

A dense cluster that relaxes to an even fill, like a settling fluid.

JS ✓ Swift ✓ Kotlin ✓
hunt designed
predator: v += seek(nearest prey)·S; prey: v += flee(nearest predator)·S

Two-species pursuit — predators seek prey, prey flee predators.

data-strengthdata-range

A two-species swarm that chases and scatters — a living ecosystem.

JS ✓ Swift ✓ Kotlin ✓
spawn designed
while engaged: emit S·2 particles/frame along the heading, each living ~90 frames

A source [S] — creates matter along the heading, budgeted by a lifespan + pool ceiling.

data-strengthdata-angle

A nozzle that jets matter into the field — a literal fountain or a star seeding the void.

JS ✓ Swift ✓ Kotlin ✓
morph weak
assign p → target t (hash of gx); v += (t − p)·k + jitter·(1 − arrived)

Matter assembles into a mark / chart / logo — never words (§11).

data-strengthdata-target

A logo or chart that assembles from drifting matter — a mark, never words.

JS ✓ Swift ✓ Kotlin ✓
resonate designed
modifier: scales sibling S by S(t) = 1 + sin(ω·t)

Pulses its sibling forces with a time-varying strength.

data-strengthdata-spin

Pair with attract: a well that breathes (pulsing strength).

JS ✓ Swift ✓ Kotlin ✓
spotlight designed
modifier: gates siblings outside an angular cone of the heading

A directional gate — confines sibling forces to a beam.

data-angle

Pair with stream: a directed beam confined to a cone.

JS ✓ Swift ✓ Kotlin ✓
screen designed
modifier: other bodies' force on matter inside r is scaled by clamp(1 − S·(1 − d/r)², min, 1)

A quiet zone — attenuates other bodies' forces inside its radius (shield, never global).

data-strengthdata-rangedata-screen-min

A reading column the field quiets down around — text shielded from a noisy page.

JS ✓ Swift ✓ Kotlin ✓
pigment transport
on overlap: c_p ← mix(c_p, tint, rate); the color advects with matter

Conserved color transport — matter takes on and carries a tint.

data-rangedata-color

A section that stains passing matter its own color, carried away.

JS ✓ Swift ✓ Kotlin ✓
fieldflow transport
n̂ = netField/|netField|; v += (n̂·|v| − v)·k_steer + n̂·gain·a_accel, gain = S·(1 − d/r)

Follow the field lines — steer onto and stream down the net field a body radiates.

data-strengthdata-range

A magnet or charge whose field lines the swarm threads, like plasma along a solar prominence.

JS ✓ Swift ✓ Kotlin ✓
relief transport
Φ = G·h(x,y) from an addField channel; v += −∇Φ·S·(1 − d/r) (data-spin < 0 ⇒ uphill)

Transport down a host-declared potential — downhill flow over terrain height.

data-potentialdata-strengthdata-rangedata-spin

Drain a map: matter settles into the valleys of the terrain your app already owns.

JS ✓ Swift ✓ Kotlin ✓
warp designed
within absorbR: p ← pairCentre + R(twist)·û·(absorbR·scale + 6); v ← R(twist)·v

A wormhole throat — relocates matter (conserved) to its data-pair body, twisted/scaled.

data-absorbdata-pair

A pair of portals: matter that enters one throat emerges from its partner, conserved.

JS ✓ Swift ✓ Kotlin ✓

Presets — composites, not new engine code

data-preset expands to several co-located bodies — one primitive each, sharing the element's rectangle. It is pure composition: the engine never grows to add one, which is why a preset needs no support row of its own beyond the tokens it composes (all of which ship on all three planes).

HTML
<div data-preset="blackhole"></div>
blackhole

A well, an accretion disk, an event horizon, and lensing.

attractswirlsinklens

A dramatic focal element — the field pours in, circles, and is swallowed.

whitehole

An emission horizon that throws matter out.

repelstream

A source that only emits — the field is pushed away and downwind.

wormhole

Two linked throats — matter entering one is relocated to its pair and thrown clear.

attractwarprepel

A pair of linked doorways — the field pours into one and erupts from the other.

star

Hydrostatic equilibrium — gravity balanced by thermal pressure.

gravitythermal

A balanced body — gravity’s pull held off by thermal pressure.

quasar

An accreting black hole with polar jets.

attractswirlsinklensjet

An accreting core with bright jets firing from its poles.

galaxy

A spiral disk that settles into a plane.

attractswirlviscositylens

A spiral that winds the field into arms and settles into a disk.

nebula

A warm, slow cloud with rising wisps.

thermalviscositybuoyancy

A warm, slow cloud — the field churns and light wisps rise.

tornado

A funnel with an updraft, calmed at the edges.

swirlstreamviscosity

A funnel that swirls the field up its axis, ragged at the edges.

fountain

A class-[S] source jetting matter up, arcing home under gravity.

spawngravity

A literal fountain — matter sprays upward and falls back in an arc.

Feedback channels — what the field writes back

Reciprocity has two halves. Bodies bend the field through data-*; the field bends them back through feedback channels — a plain-data record the engine produces for every opted-in body, every frame. The record is identical on all three planes. Only the delivery differs, and that difference is the single most important thing to know before porting a design:

PlaneHow the channels reach youChannels
JSCSS custom properties on the element (`--d`, `--load`, …), or a `feedbackSink`9
Swiftthe `onFeedback: (FeedbackChannels) -> Void` closure, or a `FeedbackSink`7
Kotlina `FeedbackSink` / the `StateRegistry` (no per-body callback on `Modifier.fieldBody`)7

On the web the channels arrive as CSS custom properties on the element itself, so a reaction is written in CSS and never touches JavaScript. On Swift and Kotlin there is no CSS to write into, so the same record arrives as a value — a closure parameter or a registry entry — and you drive the view from it.

CSS
/* the reciprocal half: the field writes, your CSS reads */
[data-feedback] {
  /* --d rests as a small fraction — amplify once, here, not in every consumer */
  --d-amp: clamp(0, calc(var(--d, 0) * 12), 1);
}
article[data-feedback] h2 {
  font-variation-settings: 'wght' calc(400 + var(--d-amp) * 300);
  opacity: calc(0.6 + var(--d-amp) * 0.4);
}

The channels

coherence CSS --coherence JS ✓ Swift ✓ Kotlin ✓
Measured local order ∈ [0,1] (= 1 − entropy; velocity alignment). Numeric — not the --coherence palette color on :root.
density CSS --d · --field-density JS ✓ Swift ✓ Kotlin ✓
The body's own gathered density ∈ [0,1], eased. The canonical reaction var.
entropy CSS --entropy JS ✓ Swift ✓ Kotlin ✓
Measured local disorder ∈ [0,1] — velocity-direction dispersion, gated by agitation (physics workover v0.3). Engine-measured; distinct from the platform's inferred --field-entropy lane.
heatmapDensity CSS --field-heatmap-density JS ✓ Swift ✓ Kotlin ✓
The ambient heatmap density under the body ∈ [0,1] — where matter pools around it, distinct from --d.
lit CSS --lit JS ✓ Swift ✓ Kotlin ✓
Spillover-lit density when a saturated neighbour bleeds density across a boundary.
load CSS --load JS ✓ Swift ✓ Kotlin ✓
A sink's accretion fill fraction ∈ [0,1] — rises as the sink captures matter, peaks at data-max, then supernovas (releases). The live level indicator for a capturing body.
next CSS --field-next JS ✓ Swift — Kotlin —
Set to 1 on the ONE body Tab would reach next from the focused one (#943), and REMOVED (not zeroed) when it stops being that body — so a plain [style*="--field-next"] selector works. A keyboard user travels a sequence the page never shows them; this makes the next stop styleable. It follows the real tab order (positive tabindex first, ascending, then natural document order), takes a [data-hot] card's place in that order from its focusable descendant rather than the container's own tabIndex, and never wraps: the last body's successor is the browser chrome. A VALUE, not a movement — it is the static equivalent the reduced-motion contract requires, and it works under render: 'none', which is the default and where the drawn current would be invisible.
pulse CSS --field-pulse JS ✓ Swift — Kotlin —
Transient flare ∈ [0,1] from field.pulse(body) — the "something just happened here" channel, deliberately separate from --d. Starts at the pulse energy (no ease-in), halves every 0.15s, and is written only while a flare is live plus one final exact 0; an un-pulsed body never carries the property. The decay runs on WALL time, not the motion budget, so a reduced-motion field still resolves its flares to rest instead of latching. Energy is additive and saturates at 1.
temperature CSS --temperature JS ✓ Swift ✓ Kotlin ✓
Measured local agitation ∈ [0,1] — half mean heat, half normalized kinetic energy.
Two channels the web adds on top. --field-density is a second custom property carrying the same value as --d (they are written in one sink call and never disagree — see the canonical feedback-channels note), and --mass is a retired name: no engine path writes it any more — only the lintFeedbackEngineOwned rule still reserves it so an old stylesheet that sets it is flagged. Style off --load.

Reading --d: the density signal, and how to scale it

Density is the channel most pages reach for first, and the one most often read wrongly — so this is the one place the story is told in full (it used to be told, inconsistently, on six pages). Resting --d is small by design. The value is an eased density in 0..1, but at rest it sits in the low fractions — often around 0.04–0.05. It is not a normalized "how lit is this body" gauge you can map straight onto 0..1 opacity; it is the raw fraction of the pool a body has gathered, kept calm on purpose so ambient drift doesn't flicker your type.

What moves it: the body's per-frame matter count within range (driven by the field's density option and the body's strength / range — a stronger, wider body gathers more), plus a fixed bump while the body is engaged (data-active / hover adds a large step). Concretely the eased target is roughly count / 20 + (engaged ? 0.45 : 0), clamped to 0..1 — so a resting body with a handful of nearby particles lands near 0.05, and engagement is what pushes it toward the top of the range. Raising the field's density or the body's strength lifts the resting floor, but only so far: the design intent is a quiet baseline, not a hot one.

So amplify in CSS rather than expecting a pre-scaled 0..1. Scale --d once into a derived variable, then drive every reaction off that — the magic number lives in one place instead of being sprinkled through the stylesheet:

CSS
/* --d (and its namespaced twin --field-density) rests SMALL by design —
   fractions of 1. Scale it once, here, instead of sprinkling a magic
   multiplier through every consumer. clamp() gives a calm floor and a
   hard ceiling. */
[data-feedback] {
  --d-amp: clamp(0, calc(var(--d, 0) * 12), 1);   /* raw fraction → ~0..1 */
}

/* drive the visible reaction off the amplified value, not the raw one */
.hero-mass {
  transform: translateY(calc(var(--d-amp) * -8px));
  opacity: calc(0.4 + var(--d-amp) * 0.6);
}

Tune the multiplier to the resting --d you actually observe for your field's density and body parameters — there is no universal constant, because the resting count depends on your scene. For a binary "is this body engaged" gate that needs no scaling, read the engine's thresholded data-field-density="high" state attribute instead of the numeric var.

The --field-* metric lane

Beside the channels above there is a second family of custom property: the --field-* metrics, 15 names enumerated from the engine's own FIELD_OUTPUT_VARS (this page fails its build if one of them is undocumented). They are web-only — the ports have no equivalent lane — which is why these rows carry no support chips.

Not all of these are live, and the difference matters. The channels above are written on any [data-feedback] body with nothing else attached. Of the metric lane, exactly one behaves that way: --field-density, which is the namespaced twin of --d. Five more are written by the pattern metric pipeline — only while a pattern, a bindData binding or bindFieldNav is attached. The remaining 9 are reserved names that nothing writes yet. This page said otherwise until Phase 6 (#1001): it described the whole lane as written every frame, which sent readers to style off variables that are permanently undefined. Each group below says which it is.

Written by the engine — automatic

The feedback sink writes this on every [data-feedback] body, every frame, with nothing else attached. It is the only name in this lane you get for free.

--field-density 0–1 · every frame, on any [data-feedback] body
Eased fraction of nearby matter gathered at this body — the primary field-intensity signal. The canonical raw channel is `--d`; this is its longer, field-namespaced form, written in the same sink call and holding the same value. High density means the body is a convergence point. Use for Drive visual weight, font size, bloom, or z-elevation. The most reliable general-purpose readback, and the only one of this lane you get for free. Scale it first — it rests small.

Written by the metric pipeline — only while a pattern or binding is attached

These are COMPUTED lanes: `applyPattern`, `bindData` and `bindFieldNav` bind them, and then they update every frame. On a body with no pattern attached they are never written, so always supply a `var()` fallback.

--field-attention 0–1 · every frame while a pattern or binding is attached
Interaction-weighted attention at this body — a composite of recency, dwell and interaction events, produced by the platform metric pipeline (a COMPUTED lane). `bindFieldNav` supplies it for navigation chrome; `applyPattern` / `bindData` supply it when a pattern declares it. Use for Distinguish hot items (read, clicked, hovered) from cold ones without a separate state tracker. Give it a `var()` fallback: a plain feedback body never receives it.
--field-entropy 0–1 · every frame while a pattern or binding is attached
Disorder of the local field, as the metric pipeline infers it. Distinct from `--entropy` (no `field-` prefix), which is the engine-MEASURED velocity-direction dispersion written by the feedback sink. Two different numbers; keep them apart. Use for Show field stability; trigger a warning when a layout becomes chaotic. Read `--entropy` instead if you want the engine's own measurement.
--field-coherence 0–1 · every frame while a pattern or binding is attached
The inverse of entropy in the inferred lane — how aligned the local relations are. Distinct from `--coherence`, the engine-measured velocity alignment (and, on `:root`, from the palette colour of the same name). Use for Validate that a pattern produces stable behaviour; surface consensus vs conflict.
--field-memory 0–1 · every frame while a pattern or binding is attached
Decaying trace of past attention — a long-window integral that fades when the body is ignored, and a COMPUTED lane. Distinct from `--field-attention`, which is current-weighted. `bindFieldNav` supplies it to mark visited links. Use for Show recency or staleness — "you read this", stale badges, fade-out.
--field-pressure 0–1 · every frame while a pattern or binding is attached
Local contention at this body, a COMPUTED lane of the metric pipeline: how strongly competing relations pull on it. Not the same thing as the raw net force vector. Use for Surface overloaded zones in dashboards; flag items under competing forces.

Reserved names — nothing writes them yet

The output catalog reserves these for the agent layer, and no shipped code path emits them. They are documented so you recognise them, and so nobody styles off a variable that is permanently undefined. Treat them as a declared contract, not a live channel.

--field-heat 0–1 · only when the host supplies data-field-heat
`heat` is NOT one of the metric kinds the pipeline computes (`packages/dom/src/metrics.ts`), so this is a *designed* lane: declared, but inert unless a host supplies it. The engine does measure per-particle heat — that reaches a body as the `temperature` channel (`--temperature`), which is a different variable. Use for Supply it yourself, or read `--temperature` instead. `lintInertFeedback` flags a binding to this lane that nothing writes.
--field-pull-x -∞–∞ (px/frame) · nothing writes it yet
The net horizontal force on this body. Reserved name; nothing emits it. The value is available from code — `sample(x, y)` returns the net force vector at a point. Use for Not usable in CSS today; read `sample()` from the handle instead.
--field-pull-y -∞–∞ (px/frame) · nothing writes it yet
The net vertical force on this body. Reserved name; nothing emits it. Use for Not usable in CSS today; read `sample()` from the handle instead.
--field-attention-share 0–1 · nothing writes it yet
A body's fraction of the total attention budget. Reserved name; no shipped code path emits it. The conserved-attention allocator that would feed it (`allocateAttention`) is a pure function you call yourself. Use for Not usable today — call `allocateAttention()` and write your own variable.
--field-attention-rank 0–N (integer) · nothing writes it yet
Ordinal rank of this body by attention within the field. Reserved name; nothing emits it. Use for Not usable today.
--field-path-use 0–1 · nothing writes it yet
How often a body sits on a high-traffic path through the field graph. Reserved name; no shipped code path emits it. Use for Not usable today.
--field-user-wake 0–1 · nothing writes it yet
A pure recency-of-touch signal: rises on interaction near the body, decays quickly. Reserved name; nothing emits it. Use for Not usable today.
--field-layout-shift 0–1 · nothing writes it yet
Normalised magnitude of the most recent position displacement. Reserved name; nothing emits it. `withFlip()` performs the FLIP reflow it would describe, but writes no such variable. Use for Not usable today.

Conditional gates — data-when

Gate any force on a condition; it acts only while the condition holds. The selective gates are evaluated per particle, so one body can pull only the fast matter passing it. Full parity: all 6 gates exist on all three planes, resolved from the same built-in registry.

active per body JS ✓ Swift ✓ Kotlin ✓
Only while the body is engaged..
fast per particle JS ✓ Swift ✓ Kotlin ✓
Only on fast-moving matter (v² > 0.9)..
slow per particle JS ✓ Swift ✓ Kotlin ✓
Only on calm matter (v² < 0.22)..
hot per particle JS ✓ Swift ✓ Kotlin ✓
Only on energized matter (heat > 0.3)..
cool per particle JS ✓ Swift ✓ Kotlin ✓
Only on calm, un-energized matter (heat < 0.08)..
scrolling per body JS ✓ Swift ✓ Kotlin ✓
Only while the page is scrolling..

An empty gate (the default) always passes, and an unknown gate name also passes — a typo weakens nothing silently, it just stops gating. The built-in set is the whole set: there is no public API for registering a condition of your own. createRegistry().condition() exists, but createField builds its registry internally and takes no registry option, so a custom gate cannot reach a field you created. Gate on state you already control instead — toggle data-when, or swap the token list.

Global formations

A formation is a bias applied to every free particle at once — the whole-field weather, orthogonal to what any individual body does. Set it declaratively with <field-root formation> or live with setFormation; the engine eases between presets so transitions glide rather than snap. Full parity across the planes.

ambient resting drift JS ✓ Swift ✓ Kotlin ✓
drift 0 · wander 1 · orbit 0.1 · spread 0 · converge 0
wells matter pools JS ✓ Swift ✓ Kotlin ✓
drift 0 · wander 0.7 · orbit 0.85 · spread 0 · converge 0
lanes a current carries JS ✓ Swift ✓ Kotlin ✓
drift 0.55 · wander 0.5 · orbit 0 · spread 0 · converge 0
scatter energy dispersed JS ✓ Swift ✓ Kotlin ✓
drift 0 · wander 1.7 · orbit 0 · spread 0.6 · converge 0
accretion everything gathers JS ✓ Swift ✓ Kotlin ✓
drift 0 · wander 0.6 · orbit 0.4 · spread 0 · converge 0.6

Render modes — the underlay

The render mode is how the field draws, set declaratively as <field-root render> (or the render option, or setRender live). It changes nothing about the physics.

This is the one part of the declarative surface where a capability can go missing in two different places, so each entry carries two rows. Accepted is whether the plane's engine takes the mode at all; Drawn is whether that plane's declarative host actually paints it. They are not the same question: the Compose FieldView once declared a render enum of its own and drew four of the seven modes its core accepted, so three were unreachable from a Compose app — the same shape of gap as the palette an Android host once collapsed to a single accent. Both are closed, and these rows are what keeps them closed.

none accepted JS ✓ Swift ✓ Kotlin ✓
Signals-only — the simulation and every signal (feedback vars, events, scrollV) stay live while MATTER stops being drawn. A field created with render: 'none' never acquires a 2d context for the underlay canvas; switching to 'none' at runtime stops the matter draw from the next frame and keeps the context it already has. Overlay readings are unaffected — they draw on their own surface (forces-system.md 13.7, amended by Field Surfaces); with no reading declared, the default, nothing is drawn at all.
dots accepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
The default — each particle a soft dot, cool centre → warm edge → accent.
trails accepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
Light-painting — particle history persists and fades.
metaballs accepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
A liquid iso-surface — the swarm rendered as one molten skin via marching squares, not dots.
voronoi accepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
Shattered glass — each particle owns a cell; the walls are the boundaries between nearest-neighbour regions.
streamlines accepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
Draws the force field itself — a grid of arrows along the net push. A diagnostic view, REPLACES the dots.
flow accepted JS ✓ Swift — Kotlin — drawn by the host JS ✓ Swift — Kotlin —
The dots AND the streamline arrows together in the one underlay canvas — particles drifting along the visible flow. No second blended surface, so it stays cheap.
knockout accepted JS ✓ Swift — Kotlin — drawn by the host JS ✓ Swift — Kotlin —
Figure-ground inversion — the field paints a solid accent wash and matter is punched out as negative space (a print knockout). Clip the canvas to real type with a CSS mask for the field-inside-letters treatment; matter never assembles into letterforms.
redshift accepted JS ✓ Swift — Kotlin — drawn by the host JS ✓ Swift — Kotlin —
Dots tinted by spectral shift instead of the heat ramp — Doppler from each particle's radial velocity (receding reds, approaching blues) plus a gravitational red near body wells. The relativistic accretion-disk look.
blackbody accepted JS ✓ Swift — Kotlin — drawn by the host JS ✓ Swift — Kotlin —
Dots tinted by energy on a thermal ramp — near-black ember → deep red → orange → warm white → blue-white — with brightness rising with temperature. Physically-warm color, not radiometry.
depth accepted JS ✓ Swift — Kotlin — drawn by the host JS ✓ Swift — Kotlin —
The z lane made visible (2.5D) — far-to-near painter's sorting so near matter occludes far, perspective parallax toward the viewport centre, and defocus with distance. Pairs with a depth > 0 field; identical geometry to dots when flat.
lic accepted JS ✓ Swift — Kotlin — drawn by the host JS ✓ Swift — Kotlin —
The force field as TEXTURE rather than as arrows (#671) — line-integral convolution. Where streamlines draws one arrow per lattice cell, this traces a few hundred short streamlines from a noise-seeded lattice and strokes each at a brightness taken from the noise, so neighbouring hairs band and clump: you stop reading individual vectors and start seeing the flow, the way iron filings show a magnet. REPLACES the dots, as streamlines does. Not textbook per-pixel LIC — that is ~2M streamline integrations a frame — but the standard sparse approximation; hairs retrace on the body-measure cadence and draw from cache every frame, so the texture never flickers. Deterministic: the lattice is a pure function of the viewport, so a seeded field renders identically every run.

The ports draw exactly this shared vocabulary — no host-local extras. Compose used to add a glow mode of its own that no other plane defined and no conformance test covered; it is retired in favour of the dots mode with a raised particleGlow, which is the same soft additive bloom expressed in the shared vocabulary.

Overlay readings — the diagnostics surface

Readings are drawn in front of content on a second canvas, and they are additive: pass one, or a space-separated stack in <field-root overlay>, and they compose. All 9 readings are declared on all three planes. On the web, an overlay is a silent no-op unless the field was created with an overlayCanvas — <field-root> manages that canvas for you; a bare createField does not.

off JS ✓ Swift ✓ Kotlin ✓
Clears the overlay surface — the default, and what an empty stack resolves to. Pass it to setOverlay to remove every reading without touching the underlay render mode.
streamlines JS ✓ Swift ✓ Kotlin ✓
Arrows along the net push a still probe would feel — vector flow, felt.
force-vectors JS ✓ Swift ✓ Kotlin ✓
The same arrows scaled by raw magnitude — strong forces read strong, weak stay faint.
field-lines JS ✓ Swift ✓ Kotlin ✓
Arrows along the structure-only field (dipoles / monopoles) — the geometry, not the felt push.
grid JS ✓ Swift ✓ Kotlin ✓
A reference lattice displaced by the local field — space itself made visible, bending where the field is strong.
temperature JS ✓ Swift ✓ Kotlin ✓
Iso-contour rings of accumulated particle heat — the thermal field, drawn as lines so it never paints over content.
energy JS ✓ Swift ✓ Kotlin ✓
Iso-contour rings of kinetic energy (½m·|v|²) — where the motion is.
path JS ✓ Swift ✓ Kotlin ✓
Streamline curves traced from seeded probes — where the field would carry a particle over distance.
data JS ✓ Swift ✓ Kotlin ✓
A numeric density readout beside each measuring body — the --d measurement made legible.

Palettes

The color template the travelling accent journeys through — set it declaratively as <field-root palette> or live with setPalette(name). A palette is presentation, not physics; a plane whose host collapses one to a single accent is a host gap, which is exactly the class of drift the parity matrix now tracks.

ours
#4da3ff #2dd4bf #a78bfa #ff6e9c #ff9d5c
heatmap
#2b3a8c #2d8fd4 #3fd07a #ffe14d #ff8a3d #e23b3b
infrared
#2a0a4a #8e24aa #e53935 #ff8f00 #ffd54f #fffde7
spectrum
#ff3b30 #ff9500 #ffcc00 #34c759 #00c7be #007aff #af52de

The custom elements

Two elements ship. <field-root> is the page field — it mirrors every createField option as a kebab-case attribute, and setting one after mount calls the matching setter on the live handle, so markup and runtime control are the same surface. <field-cell> is something else entirely: a standalone, in-frame poster that renders one force with its own small particle pool, its own budget and its own in-view gating. A page can carry dozens without them sharing or starving one pool. Both are web-only by construction.

<field-root>

accent option accent
Travelling accent color hex. Mirrors the accent createField option.
density option density
Particle-count multiplier. Mirrors the density createField option.
waves option waves
Draw background Currents. Mirrors the waves option (opt-in boolean attribute, #979 — presence = on, absence = off).
depth option depth
Optional z volume for a shallow 3D effect. Mirrors the depth createField option.
integrator option integrator
Integration scheme: 'fixed' opts into the partially frame-rate-corrected integrator (dt-scales the decays, not force impulses — doc-04 §Step 3), 'velocity-verlet' into the second-order Verlet scheme (#659); default 'legacy'. Mirrors the integrator createField option. Experimental.
render option render
Underlay render mode (none / dots / trails / links / metaballs / voronoi / streamlines / flow / knockout / redshift / blackbody / depth). Mirrors the render createField option.
overlay option overlay
Overlay reading(s) drawn in front of content — one or space-separated stack. Mirrors the overlay createField option.
palette option palette
Accent color template (built-in name or hex stops, space-separated). Mirrors the palette option.
mass option mass
First-class mass (boolean). Mirrors the mass createField option.
attention option attention
Conserved attention (boolean). Mirrors the attention createField option.
causality option causality
Cross-boundary causality (boolean). Mirrors the causality createField option.
heatmap option heatmap
Density heatmap (boolean). Mirrors the heatmap createField option.
dpr-cap option dprCap
Backing-store DPR ceiling (number). Mirrors the dprCap createField option.
grid-warp option gridWarp
Grid overlay distortion multiplier. Mirrors the gridWarp createField option.
grid-intensity option gridIntensity
Grid overlay stroke intensity. Mirrors the gridIntensity createField option.
theme option theme
Ambient theme preset (warm / cool / mono). Mirrors the theme createField option.
gradient-cool option gradientCool
Cool-end hex for the particle heat ramp. Mirrors the gradientCool createField option.
gradient-warm option gradientWarm
Warm-end hex for the particle heat ramp. Mirrors the gradientWarm createField option.
wave-baseline option waveBaseline
Hex stops for the background-wave baseline (space-separated). Mirrors the waveBaseline createField option.
wave-style option waveStyle
Wave current pattern (linear / circular). Mirrors the waveStyle createField option.
wave-center option waveCenter
Center coordinate for circular waves (space-separated x y). Mirrors the waveCenter createField option.
separation option separation
Short-range particle separation strength. Mirrors the separation createField option.
ambient-orbit option ambientOrbit
DECLARED resting-formation swirl on attract (#978); 0 for a purely radial attract. Mirrors the ambientOrbit createField option.
ambient-wander option ambientWander
DECLARED resting-formation drift (#978); lower for a calmer rest, 0 to still it. Mirrors the ambientWander createField option.
resting-motion option restingMotion
The resting-motion floor (declared, default OFF): 'thermal' | 'flow', optionally followed by a strength multiplier ('flow 0.5'). Honest idle motion for a drawn field with nothing painted; nothing under reduced motion. Mirrors the restingMotion createField option. Construction-time.
background option background
Substrate background (opaque / transparent). Mirrors the background createField option.
formation option formation
Global formation preset name. Mirrors the formation createField option (setFormation).
overlay-blend option overlayCanvas
CSS mix-blend-mode of the front overlay canvas (default 'screen'). Host placement, not a createField option — the element passes it to createOverlaySurface from @fundamental-engine/dom; applied live to the existing surface, never a rebuild. Must be a single CSS keyword; anything else falls back to 'screen' (#721).
overlay-z option overlayCanvas
CSS z-index of the front overlay canvas (default 5). Host placement, not a createField option; applied live, never a rebuild. Must be an integer; a non-integer or non-numeric value falls back to 5 (#721).

<field-cell>

force token · default 'attract'
The single force token the cell renders: attract | repel | swirl | gravity | stream | buoyancy | tether. One force only — a cell is a poster for one verb, not a composed body.
color hex · default #4da3ff
The cell's particle accent color.
count number · default 0 (auto)
Particle-pool size. 0 — the default — auto-sizes the pool to the cell's frame area.
max-particles number · default 0 (uncapped)
Hard ceiling on the pool size (§31.19). Clamps BOTH the auto-size and an explicit count, so a cell can never exceed its declared budget however large its frame grows.
fps number · default 0 (native rAF)
Target framerate for the cell's animation loop (§31.19). 0 runs at the display cadence; a positive value throttles rAF so a page full of demo cells stays cheap.