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 33 data-* attributes, the 37 forces, the 7 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 33 of 33
Swift the SwiftUI `.fieldBody(tokens:…)` view modifier 4 of 33
Kotlin the Compose `Modifier.fieldBody(tokens, …)` modifier 4 of 33

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-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-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.

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.

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.
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.

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 the names. The per-force law, glyph, live cell and worked example live on the force cards; this table is the contract summary: what each token reads, and where it runs.

Canonical nine (9)

attract JS ✓ Swift ✓ Kotlin ✓
A soft gravity-like well, optionally bent into a spiral. data-strengthdata-range
repel JS ✓ Swift ✓ Kotlin ✓
Soft outward push — carves a void. data-strengthdata-range
swirl JS ✓ Swift ✓ Kotlin ✓
Tangential swirl with light inward retention. data-strengthdata-rangedata-spin
stream JS ✓ Swift ✓ Kotlin ✓
A steady directional current along a heading. data-strengthdata-rangedata-angle
viscosity JS ✓ Swift ✓ Kotlin ✓
Viscosity — thickens the medium, bleeding momentum. data-strengthdata-range
jet JS ✓ Swift ✓ Kotlin ✓
A conduit — draws matter in, jets it out along a heading. data-strengthdata-rangedata-angle
tether JS ✓ Swift ✓ Kotlin ✓
A tether with a rest length — holds matter at a shell radius. data-strengthdata-range
wall JS ✓ Swift ✓ Kotlin ✓
An axis-aligned bouncing wall — sparks on hard impact. box-sized — no params
sink JS ✓ Swift ✓ Kotlin ✓
Captures matter, holds it (conserved), then releases it. data-absorbdata-max

Natural & derived (8)

gravity JS ✓ Swift ✓ Kotlin ✓
True softened inverse-square — a real 1/d² law. data-strengthdata-range
charge JS ✓ Swift ✓ Kotlin ✓
The signed sibling of gravity — like repels, opposite attracts. data-strengthdata-rangedata-spin
magnetism JS ✓ Swift ✓ Kotlin ✓
The Lorentz force — curves a moving charge, doing no work. data-strengthdata-rangedata-spin
thermal JS ✓ Swift ✓ Kotlin ✓
Langevin/Brownian agitation — a real temperature in the medium. data-strengthdata-range
collide JS ✓ Swift ✓ Kotlin ✓
Elastic pairwise collision — the hard-sphere billiard force. data-strengthdata-range
diffuse JS ✓ Swift ✓ Kotlin ✓
A pheromone field — deposit a mark and follow the diffused gradient. data-strengthdata-range
propagate JS ✓ Swift ✓ Kotlin ✓
A travelling wave — particles ride the expanding front. data-strengthdata-range
memory JS ✓ Swift ✓ Kotlin ✓
The field remembers — occupancy wears in paths that pull harder. data-strengthdata-range

Designed extended (20)

lens JS ✓ Swift ✓ Kotlin ✓
Rotates velocity, preserving speed — bends paths without adding energy. data-strengthdata-rangedata-spin
gate JS ✓ Swift ✓ Kotlin ✓
A one-way membrane — passes along its heading, reflects the reverse. data-angle
buoyancy JS ✓ Swift ✓ Kotlin ✓
A constant lift/sink by density — light matter rises, dense settles. data-strengthdata-range
shear JS ✓ Swift ✓ Kotlin ✓
A laminar velocity gradient — flow grows with perpendicular offset. data-strengthdata-rangedata-angle
crystallize JS ✓ Swift ✓ Kotlin ✓
Snaps cool matter onto a lattice; melts and frees it when hot. data-strengthdata-range
align JS ✓ Swift ✓ Kotlin ✓
Steers toward a heading, preserving speed — flock alignment. data-strengthdata-rangedata-angle
wind JS ✓ Swift ✓ Kotlin ✓
Divergence-free curl-noise turbulence. data-strengthdata-range
cohesion JS ✓ Swift ✓ Kotlin ✓
Short-range pressure + mid-range pull — surface tension. data-strengthdata-range
pressure JS ✓ Swift ✓ Kotlin ✓
SPH density relaxation — incompressible even-fill via mutual repulsion. data-strengthdata-range
hunt JS ✓ Swift ✓ Kotlin ✓
Two-species pursuit — predators seek prey, prey flee predators. data-strengthdata-range
spawn JS ✓ Swift ✓ Kotlin ✓
A source [S] — creates matter along the heading, budgeted by a lifespan + pool ceiling. data-strengthdata-angle
morph JS ✓ Swift ✓ Kotlin ✓
Matter assembles into a mark / chart / logo — never words (§11). data-strengthdata-target
resonate JS ✓ Swift ✓ Kotlin ✓
Pulses its sibling forces with a time-varying strength. data-strengthdata-spin
spotlight JS ✓ Swift ✓ Kotlin ✓
A directional gate — confines sibling forces to a beam. data-angle
screen JS ✓ Swift ✓ Kotlin ✓
A quiet zone — attenuates other bodies' forces inside its radius (shield, never global). data-strengthdata-rangedata-screen-min
pigment JS ✓ Swift ✓ Kotlin ✓
Conserved color transport — matter takes on and carries a tint. data-rangedata-color
fieldflow JS ✓ Swift ✓ Kotlin ✓
Follow the field lines — steer onto and stream down the net field a body radiates. data-strengthdata-range
relief JS ✓ Swift ✓ Kotlin ✓
Transport down a host-declared potential — downhill flow over terrain height. data-potentialdata-strengthdata-rangedata-spin
warp JS ✓ Swift ✓ Kotlin ✓
A wormhole throat — relocates matter (conserved) to its data-pair body, twisted/scaled. data-absorbdata-pair

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`7
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.
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.

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. Author your own with the condition registry (see the catalog).

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 DRAWING stops. A field created with render: 'none' never acquires a 2d context; switching to 'none' at runtime stops the draw from the next frame and keeps the context it already has.
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.

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.

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.