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>
// SwiftUI: the same contract as a view modifierText("Gravity well") .fieldBody( tokens: ["attract", "swirl"], strength: 0.8, range: 240, feedback: true ) { channels in // the feedback channels, per frame — the CSS vars' native twin density = channels.density ?? 0 }
// Compose: the same contract as a ModifierText( text = "Gravity well", modifier = Modifier.fieldBody( tokens = listOf("attract", "swirl"), strength = 0.8f, range = 240f, spin = 1.2f, ),)// no per-body feedback callback on the modifier yet — read feedback through the// FeedbackSink / StateRegistry on the platform layer instead.
Plane
How a body is declared
Contract 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.
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.
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.
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 —
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
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:
Plane
How the channels reach you
Channels
JS
CSS custom properties on the element (`--d`, `--load`, …), or a `feedbackSink`
7
Swift
the `onFeedback: (FeedbackChannels) -> Void` closure, or a `FeedbackSink`
7
Kotlin
a `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
coherenceCSS --coherence JS ✓ Swift ✓ Kotlin ✓
Measured local order ∈ [0,1] (= 1 − entropy; velocity alignment). Numeric — not the --coherence palette color on :root.
densityCSS --d · --field-density JS ✓ Swift ✓ Kotlin ✓
The body's own gathered density ∈ [0,1], eased. The canonical reaction var.
entropyCSS --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.
heatmapDensityCSS --field-heatmap-density JS ✓ Swift ✓ Kotlin ✓
The ambient heatmap density under the body ∈ [0,1] — where matter pools around it, distinct from --d.
litCSS --lit JS ✓ Swift ✓ Kotlin ✓
Spillover-lit density when a saturated neighbour bleeds density across a boundary.
loadCSS --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.
temperatureCSS --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.
activeper body JS ✓ Swift ✓ Kotlin ✓
Only while the body is engaged..
fastper particle JS ✓ Swift ✓ Kotlin ✓
Only on fast-moving matter (v² > 0.9)..
slowper particle JS ✓ Swift ✓ Kotlin ✓
Only on calm matter (v² < 0.22)..
hotper particle JS ✓ Swift ✓ Kotlin ✓
Only on energized matter (heat > 0.3)..
coolper particle JS ✓ Swift ✓ Kotlin ✓
Only on calm, un-energized matter (heat < 0.08)..
scrollingper 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.
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.
noneaccepted 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.
dotsaccepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
The default — each particle a soft dot, cool centre → warm edge → accent.
trailsaccepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
Light-painting — particle history persists and fades.
linksaccepted JS ✓ Swift ✓ Kotlin ✓ drawn by the host JS ✓ Swift ✓ Kotlin ✓
Constellation — lines drawn between nearby particles.
metaballsaccepted 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.
voronoiaccepted 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.
streamlinesaccepted 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.
flowaccepted 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.
knockoutaccepted 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.
redshiftaccepted 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.
blackbodyaccepted 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.
depthaccepted 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>
accentoption accent
Travelling accent color hex. Mirrors the accent createField option.
densityoption density
Particle-count multiplier. Mirrors the density createField option.
wavesoption waves
Draw background Currents. Mirrors the waves option (opt-in boolean attribute, #979 — presence = on, absence = off).
depthoption depth
Optional z volume for a shallow 3D effect. Mirrors the depth createField option.
integratoroption 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.
Cool-end hex for the particle heat ramp. Mirrors the gradientCool createField option.
gradient-warmoption gradientWarm
Warm-end hex for the particle heat ramp. Mirrors the gradientWarm createField option.
wave-baselineoption waveBaseline
Hex stops for the background-wave baseline (space-separated). Mirrors the waveBaseline createField option.
wave-styleoption waveStyle
Wave current pattern (linear / circular). Mirrors the waveStyle createField option.
wave-centeroption waveCenter
Center coordinate for circular waves (space-separated x y). Mirrors the waveCenter createField option.
separationoption separation
Short-range particle separation strength. Mirrors the separation createField option.
ambient-orbitoption ambientOrbit
DECLARED resting-formation swirl on attract (#978); 0 for a purely radial attract. Mirrors the ambientOrbit createField option.
ambient-wanderoption ambientWander
DECLARED resting-formation drift (#978); lower for a calmer rest, 0 to still it. Mirrors the ambientWander createField option.
resting-motionoption 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.
backgroundoption background
Substrate background (opaque / transparent). Mirrors the background createField option.
formationoption formation
Global formation preset name. Mirrors the formation createField option (setFormation).
overlay-blendoption 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-zoption 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.