Skip to content

Physics Overview ​

Awaken simulates rigid-body physics with Rapier (WebAssembly) sitting behind an engine-agnostic backend, so your scene talks to a small, stable interface instead of a specific engine.

What the physics system is ​

Physics gives your objects mass, gravity, and collision. A crate falls and settles on the floor, a player capsule slides along a wall instead of passing through it, a trigger volume fires an event when something enters it. All of that is driven by Rapier (@dimforge/rapier3d-compat 0.19.3), a Rust physics engine compiled to WebAssembly. The -compat build inlines the WASM (about 2 MB) into the JavaScript, so nothing is fetched at runtime.

Awaken never calls Rapier directly. Everything goes through an engine-agnostic backend abstraction: a small interface (PhysicsBackend / PhysicsWorld in packages/runtime/src/physics/backend.ts) that describes bodies, colliders, joints, raycasts, and events in Awaken's own terms. Only one file, packages/runtime/src/physics/rapier.ts, imports Rapier. Swapping engines means writing one new file that implements the same interface; the ECS components, the Inspector, the gizmos, and the pose sync stay untouched. A Jolt backend is planned for soft-body support (it would report supportsSoftBody: true; Rapier reports false). It does not exist yet.

The three layers ​

Authoring, the abstraction, and the engine are kept apart:

  1. Components - RigidBody and Collider are ordinary ECS components you add in the Inspector. They hold authoring data (velocity, restitution, a list of collider shapes).
  2. Descriptors - at Play start the runner translates each component into engine-neutral descriptors: BodyDesc (with a BodyType), ColliderDesc, and a ShapeDesc (box, roundBox, sphere, capsule, cylinder, convexHull, or trimesh; roundBox is what a Shape 2D box becomes).
  3. Backend - the Rapier backend turns descriptors into real Rapier bodies and colliders, steps the world, and hands poses and events back.

Body types ​

Every physics body is one of three kinds, chosen automatically from your components:

Body typeWhen Awaken uses itBehaviour
dynamicEntity has a RigidBody and is not marked StaticFully simulated - gravity, forces, collisions push it around.
fixedEntity has a Collider but no RigidBody, or is marked StaticImmovable scenery. Never falls, but blocks dynamics and characters.
kinematicA character promoted by api.moveAndSlideMoved by code; pushes dynamics but ignores forces. See Character Controller.

Marking an object Static in the Inspector header forces a fixed body even if it has a RigidBody. Static scenery is built once and never re-simulated, which is what keeps a large scene cheap. See RigidBody and Static Batching.

A fixed body is not frozen in place. When a script, an animation or the gizmo moves an object whose collider is fixed, the runner re-poses that body at the end of the frame (syncColliderPoses), so a script-rotated door or a moving platform carries its collider with it. Gravity is (0, -9.81, 0) for every world and has no setting.

The Physics playground in Play: balls rolling down the ramp towards the crates, with a crate's Inspector showing its components.

Try it

Open the Physics playground template from the Welcome page and press ▶ Play: the balls roll down the ramp into the crates. Drag a crate somewhere else, press ▶ Play again, and the run changes. ■ Stop puts everything back.

When physics runs - the runner lifecycle ​

Physics only runs in Play (and in the exported game, which is always playing). In edit mode nothing is simulated; your objects sit exactly where you place them. The PhysicsRunner (packages/runtime/src/physics/runner.ts) drives the backend from the ECS across the play session:

  • prewarm() loads the Rapier WASM. The editor calls it when it starts; the exported player calls it while loading, and only when a shipped scene or prefab contains a RigidBody, Collider or Ragdoll.
  • start(world) creates the world at gravity (0, -9.81, 0), builds a body and its colliders for every Transform + Collider entity, and runs one zero-length step so api.raycast finds colliders from the first frame.
  • A late WASM does not cost the session. If the WASM has not finished loading when Play starts, the runner logs backend WASM not loaded yet and builds the world the moment it lands. A world with no collider, rigidbody or ragdoll does not load the WASM at all; the runner loads it as soon as one appears (a spawned prefab, a script adding a component, api.ragdoll).
  • step(world, dt) advances the sim, then writes each moved dynamic body's pose back into its Transform. The step is clamped to 1/30 s so one slow frame cannot explode the simulation.
  • stop() frees the physics world, releasing WASM memory.

Order within a frame ​

Scripts therefore see the post-physics transforms, and any object a script moves is pushed into its collider afterwards. Every dt here is game time: at api.timeScale = 0 (a pause card) no fixed step runs and the per-frame step advances by 0.

Physics on the fixed clock ​

By default physics steps once a frame on the frame's own dt, as it always has. That makes results depend on the frame rate: a 120 Hz phone and a 60 Hz laptop integrate the same jump with different step sizes.

Project Settings → Physics → Fixed clock moves physics onto the engine's fixed clock instead (Project Settings → Time: 60 steps a second by default, at most 5 per frame). Each fixed step runs every script's onFixedUpdate(api, dt) first, then one physics step of exactly 1 / Fixed rate seconds. A frame owed more steps than the cap slows the game down rather than skipping steps. The switch is off for every project made before it existed, so their physics is unchanged until you turn it on. See Time & the fixed clock.

Contacts are still delivered to the collision hooks once a frame, after onUpdate. A pair that starts and stops touching within the fixed steps of one frame produces no event.

The all-static fast path ​

If a scene has no dynamic bodies, no active characters and no ragdolls, nothing can move and no contacts can change (fixed bodies never collide with each other). In that case step() returns immediately and a scene of pure static scenery costs no physics time per frame. Two things still earn a step: a ray cast since the last step (Rapier rebuilds its query structure only inside a step) and a moved fixed collider. The fast path ends by itself the moment a dynamic body is spawned or a character is promoted.

Exports without physics ​

An export whose scenes and prefabs contain no RigidBody, Collider or Ragdoll leaves the Rapier WASM out of game.html, the folder export and the mobile app, about 2 MB smaller. The player then logs this build ships without physics once. Only content counts: a collider a script adds at runtime in such a game has no physics to join, and api.raycast returns null.

There is no implicit floor ​

The runner simulates the colliders your scene contains and nothing else. A dynamic body over empty space falls forever, which is what an empty scene means. Give the world a floor the same way you give it anything else: an object with a Collider and no RigidBody, which is a fixed body.

Earlier versions added an invisible 2000 m box at y = 0 and a Ground plane toggle in Render Settings to switch it off. Both are gone. It caught anything meant to fall below zero, so pits, water and kill volumes silently stopped working until you found the setting, and a physics floor was not a render setting in the first place.

In this section ​

  • RigidBody - the dynamic-body component: velocity, gravity, restitution, and how poses flow back into the Transform.
  • Colliders & Shapes - compound colliders, the shape kinds (Shape 2D among them), collision layers, convex vs triangle meshes, triggers, and the Inspector collider editor.
  • Character Controller - the kinematic capsule behind api.moveAndSlide: autostep, slope limits, and snap-to-ground.
  • Raycasts, Triggers & Events - api.raycast, sensor volumes, and the onCollision family of script hooks.
  • Joints & Motors - fixed / spherical / revolute / prismatic constraints in the backend, the ragdoll that uses them, and why you cannot author one yet.
  • Debug Overlays - the Physics debug overlay (Game view checkbox or F4): moving bodies in yellow, triggers in cyan, blockers in green, and your api.debugLine lines.
  • 2D Games - Project Settings' 2D switch (bodies held to the XY plane), Shape 2D colliders, one-way platforms, and what 2D physics does not do yet.

See also ​

Awaken: browser-native WebGPU game engine.