Skip to content

Physics Overview

Awaken3D 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 mature Rust physics engine compiled to WebAssembly and inlined into the build so nothing is fetched at runtime.

Crucially, Awaken never calls Rapier directly. Everything goes through an engine-agnostic backend abstraction - a compact interface (PhysicsBackend / PhysicsWorld) 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 future Jolt backend is planned for soft-body support (it would report supportsSoftBody: true; Rapier reports false).

The three layers

Authoring, the abstraction, and the engine are cleanly separated:

  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, sphere, capsule, cylinder, convexHull, or trimesh).
  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. That is the single most important switch for performance: static scenery is built once and never re-simulated. See RigidBody and Static Batching.

📸 Screenshot - save as img/physics-overview.png

A scene in Play: a stack of dynamic cubes (each with RigidBody + Collider) toppling onto a static floor, with the Inspector open on one cube showing its RigidBody and Collider components.

When physics runs - the runner lifecycle

Physics only runs in Play. In edit mode nothing is simulated; your objects sit exactly where you place them. The PhysicsRunner drives the backend from the ECS across the play session:

  • prewarm() loads the Rapier WASM once at editor startup (and in the exported player), so pressing Play never blocks on it. If the WASM isn't ready when Play starts, that session simply runs without physics.
  • start(world) creates the world at the configured gravity (0, -9.81, 0) and builds a body + colliders for every Transform + Collider entity.
  • step(world, dt) advances the sim, then writes each moved dynamic body's pose back into its Transform. The frame delta is clamped to 1/30 s so one slow frame can't explode the simulation.
  • stop() frees the physics world, releasing WASM memory.

Within each Play frame the order is: physics step → other systems (rotators, etc.) → scripts → collider re-pose. Scripts therefore see the post-physics transforms, and any object a script moves is pushed into its collider afterwards.

The all-static fast path

If a scene has no dynamic bodies and no active characters, nothing can move and no contacts can change (fixed bodies never collide with each other). In that case step() returns immediately - a scene of pure static scenery costs zero physics per frame. It re-activates automatically the moment a dynamic body is spawned or a character is promoted.

The implicit ground plane

By default the runner adds an invisible fixed floor whose top surface is at y = 0, so dropped objects don't fall forever before you've built a real floor. Turn it off in Render SettingsGround plane once your scene has its own floor collider. The setting is applied on Play and travels with the exported game.

In this section

  • RigidBody - the dynamic-body component: velocity, gravity, restitution, and how poses flow back into the Transform.
  • Colliders & Shapes - compound colliders, the five shape kinds, 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, motors and springs, and their current wiring status.
  • Debug Overlays - the F4 collider wireframe overlay: triggers in cyan, blockers in green, named.

See also

  • Inspector - add and tune RigidBody and Collider components.
  • Scripting API - moveAndSlide, raycast, and the collision hooks.
  • Static Batching - how the Static flag pairs render batching with fixed physics bodies.
  • Runtime API - the physics runner and backend types.

Awaken — browser-native WebGPU game engine.