type Id = Ball | Floor
bodies : List ( Id, Body )
bodies =
[ ( Ball
, Physics.sphere
(Sphere3d.atOrigin (Length.meters 0.5))
Material.rubber
|> Physics.moveTo (Point3d.meters 0 0 5)
)
, ( Floor, Physics.plane Plane3d.xy Material.wood )
]
step model =
let
( newBodies, newContacts ) =
Physics.simulate
{ onEarth | contacts = model.contacts }
model.bodies
in
{ model | bodies = newBodies, contacts = newContacts }- Pure —
simulateis a function, not a stateful world; deterministic, replayable, time-travel friendly. - Your list is the world — bodies live in
List ( id, Body ); add with(::), remove withList.filter. - Type-safe coordinates and units — built on elm-geometry and elm-units; phantom types keep
WorldCoordinatesandBodyCoordinatesapart, and forces, masses, velocities, and durations all carry units. - Compound bodies — combine shapes with
Shape.plus/minus/sumand per-shape densities; mass, center of mass, and the full inertia tensor are derived for you. - Declarative constraints and collisions — pass
constrainandcollideas functions tosimulate; the engine asks per body-pair what applies, so there's no constraint registry and no filter-group API. - Warm-started solver — feed last frame's contacts back into
simulatefor stable stacks.
Inspired by Cannon.js and Bullet — this project is an experiment in what a purely functional 3D physics engine can look like.
