Fundamental · a physics engine for interfaces
Elements have mass.
Elements attract, repel and settle like real matter, so a layout shows its
relationships instead of listing them. Nothing on this page is animated — every motion
you can see is the field resolving. You add a data-body attribute; the
runtime measures and hands back live --field-* variables you style with.
You can label everything urgent. You cannot make everything heavy. Priority becomes a force, not a flag — emphasize one thing, and the rest yield.
Change a data-* and watch the field answer.
Every control rewrites one attribute on the real element to the right — the same
data-* you'd author. The engine re-reads it and the field responds. That's the
whole surface: attributes in, motion out. Move your cursor through it, too.
data-body="attract"
A real interface you’d actually build.
An inbox of real unanswered questions, ranked by urgency — recency, votes, and views become mass. But the page holds one fixed attention budget, so brightening one ask dims the rest. No particle swarm; the measurement comes back as type and ink.
Open this example →Most interfaces store meaning, then fake its effects.
You can’t style everything as urgent — the attention budget is conserved. Express “these are related,” “the user has dwelled here,” or “this is unstable” as first-class runtime state, instead of hand-rolling badges, spinners, and bespoke animation for each. See conserved attention →
data-*you author → measureruntime → --field-*runtime writes back → you styleCSS Stop building the systems that exist because the DOM is blind.
The DOM can’t see what matters, what’s related, or what’s gone stale — so teams build that sight by hand, again and again. Fundamental gives the DOM a continuous answer, and a whole category of code stops being yours to write and maintain.
- Priority you don’t re-sort. You still author importance (a
data-strength); how it plays out is a live field reading (--d, with--field-densityas the field-namespaced alias), not a list you re-sort and re-render on every change. See it work → - “Everything is P1,” solved by a law. Attention is one conserved budget — emphasize one thing and the rest necessarily yield. The normalization pass teams enforce with scoring schemes and meetings becomes something the runtime cannot violate. See it work →
- Relationships you can read. Declared associations become field-visible structure — a Field Pattern decides when association becomes coupling, not adjacency lists and graph traversal you maintain by hand. See it work →
- Staleness without timers. Recency and decay are a force in the runtime, continuous, not a cron job or a computed column you babysit. See it work →
- One model, always current. The field re-resolves every frame, so a priority shift doesn’t fire a sort-and-re-render pass — the readout is simply already current. See it work →
Fundamental doesn’t hand you a better abstraction over these jobs. It makes the question each one answered — what matters, what’s related, what’s stale — answerable by the field — so the re-sort, the timer and the hand-kept graph go away. Your data and your rules stay yours.
Declare, influence, read, reveal, govern.
Five verbs are the whole loop. You declare host objects into the field; forces influence them through declared channels; you read the state back; projections reveal it; policy governs what may run and what an agent may read.
- DeclareHost objects become bodies — a
data-bodyattribute,addBody(), or a declared edge. - InfluenceForces act through declared channels, accumulated centrally — deterministic and replayable.
- Read
query()andsnapshot()expose the field as plain, serializable state. - RevealProjections express that state through CSS variables, DOM attributes, JSON, native views, or overlays.
- GovernPolicies, budgets, redactions, lane lints, and reduced-motion rules constrain what runs and what an agent reads.
Want the exact API? Read the docs. Want the live engine? Open the manual. Want the substrate proof? One field, two readings →
Built to be inspected.
- Zero runtime dependencies. The core engine ships with none.
- Signals-first by default. A field renders nothing unless you ask for it — the invisible field is the baseline.
- Accessible by design. Semantic HTML stays the source of meaning; under reduced motion the simulation freezes while its signals keep writing. Meaning-preservation is verified surface by surface, and CSS motion without a reduced-motion rule is linted.
- Measured, not guessed. About 0.6 ms of compute per frame at 2,000 particles × 6 bodies (Node 22, one core); fill-rate is the real budget and is measured on real hardware.
- Stable 0.x surface, cross-plane conformance. Code-verified docs, a public surface guarded against silent removal by
check:api, and shared-physics conformance across web, Apple, and Android.
37 forces. 64 patterns. 9 presets. One 0.x API.
Everything above is built from one contract: data-* in, --field-* out.
The complete force-by-force manual — every force, preset, and system behaviour, each demo running
live on the real engine — now has its own page, so the homepage can stay about the substrate.
Or jump straight to a surface: the API · patterns · the force catalog.
Built on this, running now.
Four ways in: the web packages, the native Swift and Kotlin ports, and Three.js — one physics, held to a shared conformance test.
@fundamental-engine/three Ascent Adaptive CAPM® prep on iPhone, on the App Store. The field drives readiness and pacing. Swift · FundamentalCore zachshallbetter.com A personal site where the writing itself is the field — typographic, no particle swarm. @fundamental-engine/vanilla Native parity Six forces match across JavaScript, Swift and Kotlin within a stated tolerance — checked in CI on every change to a plane. js · swift · kotlin One engine, many hosts.
The core computes the field against plain data and imports zero DOM. A thin host binds it to a host — so the same physics runs on the web, natively, in your renderer, or headless:
- Web —
@fundamental-engine/dombinds the engine to the page: every[data-body]element a body, feedback returned as--field-*variables. The primary surface — and what this page runs on. - Native — a Swift port runs the same field on Apple platforms (Metal / SwiftUI), and a
Kotlin port runs it on Android (Jetpack Compose, plus a non-Compose
View/Canvashost) — both free of the browser’s compositing ceiling. The Field Lab is the showcase: a native app to explore every force live. - Your renderer —
@fundamental-engine/threedrives aTHREE.Pointsswarm in your own WebGL scene; the read primitives (sample,readParticles,addField) bind the engine to anything that draws. - Headless — no DOM, no canvas: records become bodies and relationships become edges,
and a tool reads the field through
query()andsnapshot(). The smallest conformant host — for servers, tests, and agents.
How the hosts relate — and what’s at parity across them — in Implementations.
Make model output you can read at a glance.
A model hands you answers; it rarely shows how sure it is. Make each claim a body in the
field, and confidence becomes weight, agreement becomes proximity, and contradictions pull
apart — so a shaky answer looks shaky and its sources are one hop away, before anyone
acts on it. The interface projects that state as type and layout; an agent reads the same field as JSON through field.query(). The same deterministic math drives the Evidence and Search studies.
Everything above shows the field. These twelve never do.
Twelve ordinary page types — an inbox, a market, a backlog, a front page — run as fields over real data: no particle swarm, the measurements come back as type, ink, and anchor. Each ships its data as a committed snapshot, upgrades itself to live counts when the source is reachable, and says which mode it's in.
Add it to the stack you already have.
One package per framework — pick the one for your stack and you’re done. Each bundles the
engine (core + the DOM binding) for you; there’s nothing to wire together.
npm i @fundamental-engine/vanilla Plain TS / JS, no framework — the typed FieldField class; one call mounts a field. This card is itself a live field body: it accretes the swarm and fills as it gathers. Guide → npm i @fundamental-engine/react React — the <FieldField> component + useFieldField hook. Guide → npm i @fundamental-engine/elements Any framework or plain HTML — the <field-root> web component. Guide → npm i @fundamental-engine/three Three.js / WebGL — the swarm as a THREE.Points layer. Guide →
Own the canvas / building your own renderer? @fundamental-engine/core is the
renderer-agnostic engine — you supply a host (Core guide). You
don’t install @fundamental-engine/dom directly — the packages above already
bundle it for you.
No build step? Import from a CDN (playground / prototyping only):
import { createField } from 'https://esm.sh/@fundamental-engine/vanilla'
The full package map and an install-to-reacting-headline walkthrough live in the docs.