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.
-
hostfrozen · @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.
-
accentstring · default palette's first stop - The travelling accent color (a hex string).
-
densitynumber · default 1 - Particle-count multiplier.
-
wavesboolean · 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>.
-
waveCenterVec2 | (() => 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.
-
depthnumber · 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.
-
depthFocalnumber · 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.
-
overlayOverlayMode | 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.
-
gridWarpnumber · 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>.
-
gridIntensitynumber · 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>.
-
boundsHTMLElement · 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.)
-
massboolean · 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).
-
reactionboolean · 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.
-
palettestring | string[] · default 'ours' - Accent template — a built-in name or custom hex stops.
-
themestring · 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.
-
gradientCoolstring · default theme's - Hex for the cool (resting) end of the free-particle heat ramp — overrides the theme. <field-root gradient-cool>.
-
gradientWarmstring · default theme's - Hex for the warm (energized) end of the heat ramp — overrides the theme. <field-root gradient-warm>.
-
waveBaselinestring[] · default theme's - Hex stops for the background-wave baseline — overrides the theme. <field-root wave-baseline> (space-separated).
-
attentionboolean · default false - Conserved attention — one finite strength budget; engaging a body starves the others.
-
causalityboolean · default false - Cross-boundary causality — a saturated body spills density to its neighbours.
-
heatmapboolean · default false - Density heatmap — a glow layer of where matter pools, sampled back to bodies as --field-heatmap-density.
-
separationnumber · 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>.
-
ambientOrbitnumber · 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>.
-
ambientWandernumber · 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 =)).
-
overlayCanvasHTMLCanvasElement · 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.
-
feedbackSinkFeedbackSink · default undefined - Advanced: route per-body density/feedback writes to the platform FeedbackRegistry instead of letting the engine write the DOM (Phase D3).
-
dprCapnumber · 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>.
-
overlayBackendRenderBackend · 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.
-
policyFieldPolicy · 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' }); // draw the force field itself as arrows — a diagnostic view
createField(canvas, { render: 'streamlines' }); // focus one thing at a time — one finite strength budget,
// so engaging a body starves the others
createField(canvas, { attention: true }); // a large or low-power surface — half the particles (waves are already off by default)
createField(canvas, { density: 0.5 }); // first-class mass — particle mass ∝ size, bodies accelerate by a = F/m
createField(canvas, { mass: true }); These are creation-time. Most have a live counterpart on the
FieldHandle —
setPalette, setRender,
setAttention, setCausality — so you can change them after mount too.