API reference

createField & options frozen · @fundamental-engine/core

The lowest-level entry point. createField(canvas, { host, ...options }) starts the engine on a canvas you provide and returns the FieldHandle — a host is required (the core is renderer-agnostic; @fundamental-engine/vanilla wires the browser host for you). The web component, the FieldField class, and mountField() all wrap it.

Signature

TypeScript
import { createField } from '@fundamental-engine/core';
import { browserHost } from '@fundamental-engine/dom';

const field = createField(canvas, { host: browserHost(), ...options });   // → FieldHandle

// browser convenience — @fundamental-engine/vanilla re-exports a createField that wires browserHost() for you:
import { createField } from '@fundamental-engine/vanilla';
const field2 = createField(canvas, options);                              // host wired internally
A host is required. @fundamental-engine/core is renderer-agnostic, so createField takes a host. In the browser, use createField from @fundamental-engine/vanilla frozen · @fundamental-engine/vanilla (it wires browserHost() frozen · @fundamental-engine/dom for you) or pass host: browserHost() from @fundamental-engine/dom. The snippets below omit it for brevity.

FieldOptions

Every field is optional. Defaults keep the live field unchanged until you opt in.

host frozen · @fundamental-engine/core FieldHost · default required
The environment seam (viewport, scroll, rAF, canvas). createField throws without it — pass browserHost() from @fundamental-engine/dom, or use @fundamental-engine/vanilla / the web component, which wire it for you.
accent string · default palette's first stop
The travelling accent color (a hex string).
density number · default 1
Particle-count multiplier.
waves boolean · default false
Draw the background Currents (the wave layers + bound shimmer). Opt-in (#979): a bare field has no carrier waves; pass true for the ambient resting structure.
waveStyle 'linear' | 'circular' · default 'linear'
The wave current pattern. linear draws parallel horizontal waves; circular draws concentric orbits circling around the waveCenter or the star body. <field-root wave-style>.
waveCenter Vec2 | (() => Vec2) | null · default null
The center coordinate (or coordinate-provider fn) for circular waves. Defaults to the first body tagged with star or vortex, or the center of the field. <field-root wave-center> (space-separated, e.g. "200 300").
background 'opaque' | 'transparent' · default 'opaque'
Substrate background. 'transparent' clears to transparent instead of painting the near-black substrate, so the underlay composites over light content (an image, a 3D scene, a light page) — trails fade to transparent rather than to black. Also a <field-root background> attribute and live via setBackground.
depth number · default 0
Optional z volume. 0 (the default) is the flat field, byte-identical to the 2D engine; > 0 opens a shallow depth the matter drifts through, projected as a size/alpha recession. Purely additive — no API requires z.
integrator 'legacy' | 'fixed' | 'velocity-verlet' · default 'legacy'
Integration scheme (substrate doc 04 §Step 3, #659). 'fixed' opts into the partially frame-rate-corrected integrator: the FRICTION/HEAT_DECAY decays scale with dt (FRICTION^dt), so damping is consistent across frame rates — but by design it does NOT yet dt-scale force impulses (unsound for pair forces that mutate a neighbour in the same pass; deferred to the force-contract change, integrator.ts applyForce / doc-04 §Step 3); at the reference rate (dt === 1, and every golden/conformance run) it is byte-identical to the default 'legacy' semi-implicit Euler, so opting in never moves the golden. 'velocity-verlet' opts into the second-order velocity-Verlet scheme: the position full-step uses the previous step's stored acceleration (x += v·dt + ½·a·dt²), the force pass evaluates a′ at the updated position, and the velocity takes the half-step average (v += ½·(a + a′)·dt); kinematic velocity-replacing forces (jet/wall/lens/gate/warp) bypass the average as discontinuities, and decays are dt-scaled like 'fixed'. Higher positional accuracy, NOT a conservation guarantee (energy/momentum stay non-conserved by design; the per-step decay keeps it non-symplectic) — trajectories differ from 'legacy' by design once opted in. The default path never engages either mode. <field-root integrator>. Experimental.
render 'none' | 'dots' | 'trails' | 'links' | 'metaballs' | 'voronoi' | 'streamlines' | 'flow' | 'knockout' | 'redshift' | 'blackbody' | 'depth' · default 'none'
Render mode for the underlay surface (behind content) — the same physics drawn differently. The default is 'none' (#538): the signals-only engine runs the full simulation + feedback (--d, --load, capture events) but draws nothing — the field is behavior first. Opt into a visible surface with 'dots' (particles) or another mode.
heatCenter { x: number; y: number } · default { x: 0.5, y: 0.4 }
DECLARED render reference point (Wallpaper Rule, #975): the center of the cool→warm heat vignette the dots/depth swarm is tinted against, as viewport fractions ∈ [0,1] (resolved to px each frame). Formerly a hardcoded (W/2, H·0.4); the default reproduces it exactly, so the default render is byte-identical. Only affects the dots/depth render modes. Experimental.
redshiftObserver { x: number; y: number } · default { x: 0.5, y: 0.5 }
DECLARED render reference point (Wallpaper Rule, #975): the observer the redshift mode measures each particle's radial velocity against (receding reddens, approaching blues), as viewport fractions ∈ [0,1]. Formerly a hardcoded (W/2, H/2); the default reproduces it exactly, so the redshift render is byte-identical. Only affects the redshift render mode. Experimental.
depthFocal number · default 480
DECLARED render reference point (Wallpaper Rule, #975): the perspective focal length (CSS px) of the depth mode's camera — larger flattens the perspective, smaller exaggerates it. Formerly a hardcoded FOCAL = 480; the default reproduces it exactly, so the depth render is byte-identical. Only affects the depth render mode (pairs with depth > 0). Experimental.
heatmapFade { start: number; span: number } · default { start: 0.3, span: 0.85 }
DECLARED page-layout reference (Wallpaper Rule, #975): how the density heatmap glow fades with scroll position — full above start·H, gone by (start+span)·H (H = viewport height). Formerly a hardcoded (1.15 - scrollY/H)/0.85 "content = first viewport" assumption; the default reproduces it exactly (full through 0.3 viewports, gone by 1.15), so the heatmap glow is byte-identical. Set a large span to disable the scroll fade. Only affects the density heatmap layer. Experimental.
overlay OverlayMode | OverlayMode[] · default 'off'
Field Surfaces: the overlay READING(S) drawn in front of content (see the overlay-readings table) — one reading or an additive stack. Set alongside render. <field-root> manages the front canvas (space-separated tokens in the attribute); for createField directly, pass overlayCanvas.
gridWarp number · default 1
Distortion multiplier for the grid overlay (the reference lattice displaced by the field). 1 is the calibrated "legible, never chaotic" deflection; 2–3 exaggerates the deformation for demos; 0 flattens the lattice. Only affects the grid overlay mode. <field-root grid-warp>.
gridIntensity number · default 0.16
Stroke intensity (opacity) of the grid overlay lattice ∈ [0,1] — turns the warped grid from a faint diagnostic into a visual centerpiece (#552). 0.16 is the calibrated default; raise it (~0.4) to make the bending space the hero of a section. Only affects the grid overlay mode. <field-root grid-intensity>.
bounds HTMLElement · default window
Contained fields (#540): scope the field to a single element instead of the viewport. A vanilla createField / new FieldField option — the field measures and renders inside the given element (a card, a panel), so a component can carry its own local field. Omit for the default full-window field. (containerHost() is the lower-level door.)
mass boolean · default false
First-class mass: particle mass ∝ size and body forces accelerate by a = F/m; dynamic bodies also get inertial mass ∝ rendered area (#872).
reaction boolean · default false
Newtonian own-emission reaction (#873): a dynamic body feels the equal-and-opposite of the net impulse it imparts to nearby matter (a directional emitter recoils like a rocket). Best paired with mass. Experimental.
palette string | string[] · default 'ours'
Accent template — a built-in name or custom hex stops.
theme string · default 'warm'
Ambient theme preset — 'warm' | 'cool' | 'mono'. Sets the free-particle heat ramp and the background-wave baseline (distinct from the travelling accent). 'warm' reproduces the shipped default.
gradientCool string · default theme's
Hex for the cool (resting) end of the free-particle heat ramp — overrides the theme. <field-root gradient-cool>.
gradientWarm string · default theme's
Hex for the warm (energized) end of the heat ramp — overrides the theme. <field-root gradient-warm>.
waveBaseline string[] · default theme's
Hex stops for the background-wave baseline — overrides the theme. <field-root wave-baseline> (space-separated).
attention boolean · default false
Conserved attention — one finite strength budget; engaging a body starves the others.
causality boolean · default false
Cross-boundary causality — a saturated body spills density to its neighbours.
heatmap boolean · default false
Density heatmap — a glow layer of where matter pools, sampled back to bodies as --field-heatmap-density.
separation number · default 0
Strength of short-range particle-to-particle separation/repulsion force ∈ [0,1] to break up clumps in high-density areas. <field-root separation>.
ambientOrbit number · default 0.1
DECLARED ambient bias (Wallpaper Rule, #978): the resting ambient formation's tangential swirl injected into attract bodies — the gentle spiral free matter traces around a well at rest. Formerly a hardcoded 0.1 painted into the default formation preset; now a documented dial defaulting to that value (resting behavior unchanged). Set 0 for a purely radial attract (no spiral). Applies to the ambient formation only. <field-root ambient-orbit>.
ambientWander number · default 1.0
DECLARED ambient bias (Wallpaper Rule, #978): the resting ambient formation's wander term — the per-particle drift that keeps resting matter alive rather than frozen. Formerly a hardcoded 1.0 in the default formation preset; now a documented dial defaulting to that value (behavior unchanged). Lower for a calmer rest, 0 to still the drift. Applies to the ambient formation only. <field-root ambient-wander>.
restingMotion { mode: 'thermal' | 'flow'; strength?: number } · default undefined (off)
The resting-motion floor (DECLARED, default OFF). Signals-first defaults leave a DRAWN field (render: 'dots') settling into its attract wells and freezing when idle; this is the honest, global alternative to painting waves — a small per-particle impulse the field MEASURES as --temperature, with nothing drawn. 'thermal' is the thermal force's Langevin kick applied field-wide (Box–Muller through the injected rng, so a seeded run reproduces); 'flow' is a divergence-free curl (the stream function ψ = sin(kx+φ)·sin(ky−φ), one eddy ≈ 400px) with a slow phase drift so it never settles. strength multiplies the impulse (default 1; 0 = off). Both scale with dt, so reduced motion (dt = 0) contributes exactly nothing. Default-off ⇒ the cross-plane conformance golden is untouched. <field-root resting-motion="thermal"> / <field-root resting-motion="flow 0.5">. Mirrored on Swift (FieldOptions.restingMotion) and Kotlin (createField(restingMotion =)).
overlayCanvas HTMLCanvasElement · default undefined
Field Surfaces: a caller-provided canvas for the overlay surface (drawn in front of content). The web component creates/manages this for you; pass it only when calling createField directly.
overlayCanvasProvider () => HTMLCanvasElement | null · default undefined
Field Surfaces: a lazy alternative to overlayCanvas (#676). When no overlayCanvas is set, core calls this once — the first time an overlay reading actually goes active — to obtain the surface. Lets a host defer creating its full-viewport mix-blend canvas until an overlay is switched on, so the common overlay:off path never adds a canvas to the compositing tree at boot. <field-root> uses this internally; return null to decline. Ignored when overlayCanvas is set.
feedbackSink FeedbackSink · default undefined
Advanced: route per-body density/feedback writes to the platform FeedbackRegistry instead of letting the engine write the DOM (Phase D3).
dprCap number · default 2
Backing-store device-pixel-ratio ceiling (#410) — the dominant fill-rate lever. Effective DPR = min(devicePixelRatio, dprCap); capping at ~1.5 buys ~1.8× headroom on retina for a small perceptual softening. Runtime-settable via setDprCap. <field-root dpr-cap>.
overlayBackend RenderBackend · default Canvas 2D
Drawing backend for the overlay surface (#373) — the structural seam a WebGL/WebGPU surface implements. Defaults to the Canvas 2D implementation over overlayCanvas. See render-backend.ts.
rng () => number · default Math.random
Random source for ALL engine randomness — particle seeding, spawn scatter, jitter, release angles (#371). Supply a seeded generator to make a run reproducible (the record/replay seam).
now () => number · default performance.now
Wall-clock source for input-idle tracking (#371) — one of the three engine clocks (wall / frame / simulation). Defaults to performance.now; override in tests or replay harnesses.
policy FieldPolicy · default undefined
Runtime FIELD POLICY — what THIS host/session/user/app PERMITS at runtime (distinct lane from governance, which is what doctrine allows via static lint). FieldPolicy { allowBodyDataInSnapshots?, allowMotionProjection?, maxMotionBudget? (0..1), budgets?: Partial<FieldBudgets> }; FieldBudgets bounds consumable resources: motion/force/attention/thermal/render/privacy/accessibility/agentRead. WIRED today: the motion budget folds (via min) with reduced-motion + perf pressure into the effective motion the integrator/easing reads — reduced-motion always wins (a policy can lower motion, never raise it); the privacy budget (+ allowBodyDataInSnapshots) gates body data in snapshot(). Other budgets are declared-not-yet-enforced. Change it live with setPolicy. Purely additive — a field with no policy behaves exactly as before.
identify (el: HTMLElement) => FieldBodyIdentity | undefined · default undefined
First-class body identity resolver: derive a stable, structured FieldBodyIdentity ({ id, namespace?, kind?, host? }) from a DOM-scanned body's element, called once per body. The identity is surfaced on query()/snapshot() body readings and used as the stable key for diff/replay/relationships. Return undefined to fall back to the default derivation (the element DOM id, else a monotonic body-N). Purely additive — a field without identify behaves exactly as before.

Common configurations

Every option is optional and the defaults keep the field calm. These are the combinations worth knowing — each is the whole options object (the host is omitted; wire it per the signature above, or use @fundamental-engine/vanilla). Switch tabs to compare.

// the resting field — an accent and a palette, nothing else
createField(canvas, { accent: '#4da3ff', palette: 'ours' });
These are creation-time. Most have a live counterpart on the FieldHandlesetPalette, setRender, setAttention, setCausality — so you can change them after mount too.