Physics Overview
Web Engine integrates Rapier3D, a high-performance physics engine written in Rust and compiled to WebAssembly, providing deterministic rigid body dynamics, advanced collision detection, and character controllers.
Physics in Web Engine is powered by Rapier3D, a fast and deterministic physics engine written in Rust and compiled to WebAssembly. The engine provides rigid body dynamics, advanced collision detection, character controllers, joints, raycasting, and more. Physics can run on the main thread or offloaded to a Web Worker for improved performance.
Physics Architecture
Web Engine supports multiple physics backends through the PhysicsBridge abstraction: Server (main thread Rapier), Client (Web Worker), Simple (lightweight fallback), and Wasm (optimized WebAssembly). The system automatically selects the best available backend with graceful fallbacks.
Physics Components#
RigidBody
Makes an entity participate in physics simulation. Supports dynamic, kinematic (position/velocity-based), and static bodies with mass, friction, restitution, and CCD.
Collider
Defines collision shapes. Supports primitives (box, sphere, capsule), complex shapes (convex hull, trimesh, heightfield), sensors, and collision filtering.
CharacterController
Kinematic character controller with ground detection, slope handling, step climbing, and smooth collision response.
Joints
Connect rigid bodies with constraints: fixed, spherical (ball), revolute (hinge), prismatic (slider) joints with limits and motors.
Raycasting
Perform ray casts, shape casts, and overlap queries for line-of-sight, ground detection, and hit detection.
Physics World
Configure gravity, simulation stepping, collision groups, and performance optimizations.
Quick Start#
To make an object fall with gravity:
- Select an entity with a
MeshRenderer. - Add a
RigidBodycomponent. - Set the body type to Dynamic.
- Add a
Collidercomponent. - Choose a shape (Box, Sphere, Capsule, etc.).
- Press Play to see it fall!
Rigid Body Types#
Dynamic#
Fully simulated bodies affected by gravity, forces, and collisions. Use for anything that should move realistically.
- Affected by gravity
- Responds to forces and impulses
- Collides with other bodies
- Examples: boxes, balls, debris, ragdolls
Kinematic#
Bodies moved by code (not physics). They affect dynamic bodies but aren't affected by them.
- Not affected by gravity or forces
- Move by directly setting position/rotation
- Push dynamic bodies out of the way
- Examples: moving platforms, elevators, doors
Static#
Immovable bodies. Perfect for level geometry that never moves.
- Never move (fastest)
- Other bodies collide with them
- Examples: walls, floors, terrain
Collider Shapes#
Choose the collider shape that best approximates your mesh:
- Box — Best for rectangular objects (crates, walls).
- Sphere — Best for round objects (balls, planets). Cheapest.
- Capsule — Cylinder with rounded ends. Great for characters.
- Cylinder — Barrels, pillars.
- Convex Hull — Wraps around complex shapes. More expensive.
- Trimesh — Exact mesh collision. Only for static bodies.
Performance Tip
Simple shapes (Box, Sphere, Capsule) are much faster than complex shapes (Convex, Trimesh). Use compound colliders with simple shapes when possible.
Physics World Configuration#
The physics world can be configured with custom gravity, simulation timestep, and collision event buffer sizes. Configuration is managed through the PhysicsConfig system.
| Property | Default | Description |
|---|---|---|
| gravity | { x: 0, y: -9.81, z: 0 } | World gravity vector in m/s² |
| eventsBufferSize | 16384 | Collision event ring buffer capacity |
| raycastQueueCapacity | 1024 | Max concurrent raycast requests |
| protocolVersion | 2 | Physics bridge protocol version |
import { setPhysicsConfig } from '@web-engine/core'; // Configure custom gravity (e.g., moon gravity)setPhysicsConfig({ gravity: { x: 0, y: -1.62, z: 0 },}); // Configure larger event buffer for many collisionssetPhysicsConfig({ eventsBufferSize: 32768, raycastQueueCapacity: 2048,});Collision Groups#
Collision groups use 16-bit bitmasks to control which colliders interact. Each collider has a membership (which groups it belongs to) and a filter (which groups it can collide with).
import { CollisionGroups } from '@web-engine/core'; // Player collides with enemies and environmentconst playerGroup = CollisionGroups.create( CollisionGroups.PLAYER, CollisionGroups.ENEMY | CollisionGroups.ENVIRONMENT); // Projectile only hits enemies (not player who fired it)const projectileGroup = CollisionGroups.create( CollisionGroups.PROJECTILE, CollisionGroups.ENEMY | CollisionGroups.ENVIRONMENT);Pre-configured collision presets are available for common scenarios:
| Preset | Membership | Filter |
|---|---|---|
| PLAYER | PLAYER | ENVIRONMENT | ENEMY | INTERACTIVE | TRIGGER |
| ENEMY | ENEMY | ENVIRONMENT | PLAYER | ENEMY | PROJECTILE |
| ENVIRONMENT | ENVIRONMENT | ALL (except TRIGGER) |
| TRIGGER | TRIGGER | PLAYER | ENEMY | VEHICLE |
| GHOST | NONE | NONE (no collision) |
Scripting Physics#
Access physics in scripts through the api object:
export default (api) => { return (dt) => { // Read/write velocity const vel = api.velocity; vel.y -= 9.8 * dt; // Custom gravity api.velocity = vel; // Apply impulse (instant force) if (api.input.jumpDown) { api.applyImpulse({ x: 0, y: 10, z: 0 }); } // Apply impulse at specific point (torque) api.applyImpulseAtPoint( { x: 10, y: 0, z: 0 }, { x: 0, y: 1, z: 0 } // Point offset ); // Raycast for ground detection const hit = await api.raycast( api.position, { x: 0, y: -1, z: 0 }, 2.0 // Max distance ); if (hit && hit.distance < 1.0) { api.log(`Hit: ${hit.eid} at distance ${hit.distance}`); } };};Performance Optimization#
- Use simple shapes (Box, Sphere, Capsule) over complex shapes (ConvexHull, Trimesh) whenever possible.
- Keep static bodies for immovable geometry (walls, floors) - they're optimized for zero movement.
- Use compound colliders (multiple simple shapes) instead of a single complex mesh.
- Enable sleeping for bodies that rarely move to skip simulation.
- Use collision groups to avoid unnecessary collision checks between unrelated objects.
- Enable CCD (Continuous Collision Detection) only for fast-moving objects.
- Consider using the Client physics backend (Web Worker) to offload physics from the main thread.
Trimesh Limitation
Trimesh colliders (exact mesh collision) can only be used with static bodies. For dynamic bodies, use convex hull or compound colliders with simple shapes.