Create a static plane, collidable only from the direction of the normal, e.g. for +Z:
floor =
Physics.plane Plane3d.xy Material.woodCreate a cylinder, approximated with 12 side faces. For more subdivisions, use dynamic with Shape.cylinder.
Create a capsule (a cylinder with hemispherical end caps).
Create a cone, approximated with 12 side faces. For more subdivisions, use dynamic with Shape.cone.
Create a point mass — a body with mass but no extent. Two point masses pass through each other; with all other bodies they collide normally.
Set the position of the body, e.g. to raise a body 5 meters above the origin:
movedBody =
body
|> moveTo (Point3d.meters 0 0 5)Move the body relative to its current position, e.g. to translate a body down by 5 meters:
translatedBody =
body
|> translateBy (Vector3d.meters 0 0 -5)Rotate the body around an axis in the world, e.g. to rotate a body 45 degrees around the Z axis:
rotatedBody =
body
|> rotateAround Axis3d.z (Angle.degrees 45)Set the position and orientation of a body. Like moveTo but also sets the orientation.
placedBody =
body
|> place
(Frame3d.atPoint (Point3d.meters 2 0 1)
|> Frame3d.rotateAround Axis3d.z
(Angle.degrees 45)
)Left-handed frames are not supported — Z is recomputed from X and Y.
Advance the simulation by one fixed step of Config.duration,
returning updated bodies and contacts. With onEarth that step
is 1/60th of a second in Earth gravity:
( simulated, contacts ) =
simulate onEarth model.bodiesKeep the step constant — don’t feed it
onAnimationFrameDelta
directly, or the solver will jitter. Render in between steps with
interpolatedFrame; the companion
elm-timestep
package handles the bookkeeping.
To improve solver stability for stacked objects, pass contacts
from the previous frame back via the config’s contacts field:
( simulated, contacts ) =
simulate { onEarth | contacts = model.contacts }
model.bodiesA ready-to-use simulation config with Earth gravity pointing down (-Z) at 60 fps. Customize it by updating individual fields, see Config for available options.
Configures a simulation.
onEarth =
{ gravity = Vector3d.gees 0 0 -1
, duration = Duration.seconds (1 / 60)
, solverIterations = 20
, contacts = emptyContacts
, constrain = \_ -> Nothing
, collide = \_ _ -> True
}gravity — set the gravity vector, or Vector3d.zero for no gravity
duration — set to Duration.seconds (1 / 60) for 60 fps
solverIterations — balance between precision and performance, 20 is a sweet spot
contacts — pass Contacts from the previous frame for warm starting, or leave as default for cold start
constrain — limit body movement relative to each other, see Constraint
collide — decide which bodies can collide with each other
Contacts from the most recent simulation frame. Contains contact points and solver state for warm starting.
Empty contacts for the first simulation frame (no warm starting).
Get contact points from the most recent simulation frame, filtered by a predicate. Each entry is a pair of body ids and a list of contact points between them.
crash =
Physics.contactPoints
(\a b -> a == "jeep" && b == "wall")
contactsGet the position and orientation of the body as Frame3d. Useful to transform points and directions between world and body coordinates, e.g. for rendering.
Interpolate the frame between two simulation states for smooth
rendering. progress is the lerp factor in [0, 1]; both bodies must be
the same logical body across consecutive simulation steps.
Physics.interpolatedFrame 0.5 previousBody currentBodyGet the origin point of a body.
Get the current linear velocity of a body.
Get the current angular velocity of a body.
Get the linear velocity of a point on a body. Takes into account both linear and angular velocities.
Get the center of mass of a body. Returns Nothing for static and kinematic bodies (which have infinite mass).
Get the mass of a body. Returns Nothing for static and kinematic bodies (which have infinite mass).
Find the closest intersection of a ray against a list of bodies.
Apply a force at a point on a body.
Keep applying the force every simulation step to accelerate.
force =
Vector3d.withLength (Force.newtons 50)
Direction3d.positiveY
pushedBox =
box
|> applyForce force pointOnBoxApply an impulse at a point on a body, adding to its velocity and angular velocity.
impulse =
Vector3d.withLength
(Quantity.times (Duration.seconds 0.005)
(Force.newtons 50)
)
Direction3d.positiveY
hitCueBall =
cueBall
|> applyImpulse impulse hitPointApply a pure torque to a body — spin without translation.
Keep applying the torque every simulation step to spin up.
torque =
Vector3d.withLength (Torque.newtonMeters 5)
Direction3d.positiveZ
spinningTop =
top
|> applyTorque torqueApply an angular impulse to a body, adding to its angular velocity.
angularImpulse =
Vector3d.withLength
(Quantity.times (Duration.seconds 0.005)
(Torque.newtonMeters 50)
)
Direction3d.positiveZ
spunUp =
body
|> applyAngularImpulse angularImpulseCreate a dynamic body from shapes and materials. Mass and center of mass are derived from geometry and density.
Create a static body from shapes and materials. Static bodies only collide with dynamic bodies, not other static bodies.
Create a kinematic body from shapes and materials. Kinematic bodies have infinite mass like static bodies — forces, gravity, and contacts don’t push them — but they’re moved by the engine according to their velocity, set via setVelocityTo and setAngularVelocityTo.
Useful for moving platforms, elevators, conveyor belts, and turntables. Dynamic bodies see the kinematic’s velocity at the contact, so a box rests on a moving elevator without sliding off, and a conveyor carries crates along its surface.
elevator =
Physics.kinematic [ ( Shape.block platform, Material.steel ) ]
|> Physics.setVelocityTo (Vector3d.metersPerSecond 0 0 0.5)
turntable =
Physics.kinematic [ ( Shape.cylinder 16 disc, Material.plastic ) ]
|> Physics.setAngularVelocityTo
(Vector3d.withLength (AngularSpeed.radiansPerSecond 1)
Direction3d.positiveZ
)Change body state directly, bypassing the simulation.
Replace the linear velocity of a body. Works on both dynamic and kinematic bodies.
For dynamic bodies, prefer applyImpulse — using setVelocityTo
after applyImpulse silently discards the impulse:
body
|> applyImpulse impulse point
-- erased by the next line
|> setVelocityTo newVelocityFor kinematic bodies, this is the primary way to drive motion — the engine integrates the position from the velocity each frame.
Has no effect on static bodies.
Replace the angular velocity of a body. See setVelocityTo for guidance. Has no effect on static bodies.
Scale a body to the given mass. The volume and center of mass are preserved. Has no effect on static or kinematic bodies.
Set linear and angular damping, in order to decrease velocity over time. These parameters specify the proportion of velocity lost per second. Values are clamped to [0, 1]. Default: 0.01 for both.
Restrict a body’s degrees of freedom along world axes. The list fully
describes the lock state — calling lock again replaces the previous
masks. An empty list clears all locks.
-- 2D-in-3D gameplay on the XY plane
body |> lock [ Lock.translateZ, Lock.rotateX, Lock.rotateY ]
-- character controller: slides freely, never tips
body |> lock Lock.allRotationSee Physics.Lock for the available tokens. Has no effect on static or kinematic bodies.
Apply the inverse inertia tensor of a body to a vector. Returns Vector3d.zero for static and kinematic bodies. For common cases, see angularAccelerationFromTorque and angularVelocityDeltaFromAngularImpulse.
Compute angular acceleration from torque: α = I⁻¹τ
How fast will this torque make the body spin up?
Compute angular velocity change from an angular impulse: Δω = I⁻¹L
How much does this impulse add to my current spin?
A body is anything the simulation moves or collides — a ball, a box, a wall, a moving platform. It is defined in BodyCoordinates and positioned in WorldCoordinates. Bodies start out centered on the origin; use moveTo to set the position.
There are three kinds of bodies:
dynamic — moved by the engine in response to forces, gravity, and contacts. The default for block, sphere, cylinder, capsule, cone, and pointMass; use dynamic to combine several shapes into one body.
static — never moves. Used for floors, walls, and other immovable scenery. The default for plane; use static to combine several shapes into one body.
kinematic — moves at the velocity you set via setVelocityTo and setAngularVelocityTo; ignores forces, gravity, and contacts, but other dynamic bodies feel its motion through friction. Used for moving platforms, elevators, and turntables. Construct with kinematic.