Physics
The IWSDK provides a comprehensive physics system powered by the @babylonjs/havok physics engine. This chapter covers everything you need to know about implementing realistic physics in your IWSDK applications.
By the end of this chapter, you’ll be able to:
- Set up the Havok physics engine with customized gravity settings
- Create falling objects with realistic collision shapes and material properties
- Build static environments with floors, walls, and other immovable objects
- Implement grabbable objects with proper physics interactions
- Apply forces and manipulate object velocities for dynamic interactions
- Optimize physics performance for complex scenes
The physics system is built using an Entity Component System (ECS) architecture and consists of three main components:
PhysicsSystem - The core system that manages the Havok physics worldPhysicsShape - Defines collision shapes and physics material properties (density, restition, friction)PhysicsBody - Defines the motion behavior of entities (Static, Dynamic, Kinematic)PhysicsManipulation - Applies one-time force and velocity changes
Here’s a minimal example to get physics working in your scene:
import {
World,
PhysicsSystem,
PhysicsBody,
PhysicsShape,
PhysicsState,
PhysicsShapeType,
Mesh,
SphereGeometry,
MeshStandardMaterial,
} from '@iwsdk/core';
// 1. Register the physics system with customized gravity
world
.registerSystem(PhysicsSystem, { configData: { gravity: [0, -10, 0] } })
.registerComponent(PhysicsBody)
.registerComponent(PhysicsShape);
// 2. Create a mesh
const sphere = new Mesh(
new SphereGeometry(0.5),
new MeshStandardMaterial({ color: 0xff0000 }),
);
sphere.position.set(0, 5, 0);
scene.add(sphere);
// 3. Create entity and add physics components
const entity = world.createTransformEntity(sphere);
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto, // Automatically detects sphere
});
entity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic, // Falls due to gravity
});
Step 1: Register the Physics System
The PhysicsSystem must be registered with your world to enable physics simulation:
world
.registerSystem(PhysicsSystem)
.registerComponent(PhysicsBody)
.registerComponent(PhysicsShape);
Step 2: Physics World Configuration
The physics system automatically creates a Havok physics world with:
- Gravity: Default value
[0, -9.81, 0] (configurable in physics system) (Earth-like gravity) - Step Rate: Synchronized with your application’s frame rate
- Automatic Cleanup: Physics resources are cleaned up when entities are removed
Understanding the Components
Defines the collision shape and material properties of an entity.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [2, 1, 1],
density: 1.0,
friction: 0.5,
restitution: 0.0,
});
dimensions - Shape-specific dimensions array. Not applicable when PhysicsShapeType.Auto is used.density - Mass density (default: 1.0)friction - Surface friction coefficient (default: 0.5)restitution - Bounciness factor (default: 0.0)
Auto Detection
The most convenient option that automatically detects the best shape from your Three.js geometry. When this type is selected, the dimensions field in PhysicsShape will be overridden by the size of the Three.js geometry.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto,
});
Mapping Rules:
SphereGeometry → SphereBoxGeometry → BoxPlaneGeometry → Box (thin)CylinderGeometry → Cylinder- Other geometries →
ConvexHull
Sphere
Most efficient for round objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0], // [radius, unused, unused]
});
Box
Perfect for rectangular objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [2, 1, 1], // [width, height, depth]
});
Cylinder
For cylindrical objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Cylinder,
dimensions: [0.5, 2, 0], // [radius, height, unused]
});
ConvexHull
A convex hull is the smallest convex shape containing points. It has good balance between accuracy and performance for complex shapes.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.ConvexHull,
// dimensions automatically calculated from geometry
});
TriMesh
Using tri mesh to fit the geometry. Most accurate but computationally expensive. Best for static objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.TriMesh,
// dimensions automatically calculated from geometry
});
Physics Material Properties
Controls the mass of the object. Higher density = heavier object.
// Light object (foam ball)
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
density: 0.1,
});
// Heavy object (metal ball)
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
density: 10.0,
});
Controls sliding behavior on surfaces (0 = no friction, 1 = high friction).
// Slippery ice surface
iceEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [10, 0.1, 10],
friction: 0.1,
});
// Grippy rubber surface
rubberEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [10, 0.1, 10],
friction: 0.9,
});
Controls bounciness (0 = no bounce, 1 = perfect bounce).
// Dead ball (no bounce)
deadBallEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
restitution: 0.0,
});
// Super bouncy ball
bouncyBallEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
restitution: 0.95,
});
Defines the motion behavior of an entity in the physics simulation.
entity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic, // Required: motion type
});
PhysicsState.Static - Immovable objects like walls and floors
- Never moves, but affects other bodies
- Ideal for environment geometry
PhysicsState.Dynamic - Responds to forces and gravity
- Affected by collisions, gravity, and applied forces
- Most common for interactive objects
PhysicsState.Kinematic - Programmatically controlled
- Can be moved by code but not affected by other bodies
- Useful for moving platforms or player-controlled objects
// Static floor
floorEntity.addComponent(PhysicsBody, { state: PhysicsState.Static });
// Dynamic ball that falls and bounces
ballEntity.addComponent(PhysicsBody, { state: PhysicsState.Dynamic });
// Kinematic elevator platform
elevatorEntity.addComponent(PhysicsBody, { state: PhysicsState.Kinematic });
The PhysicsManipulation component allows you to apply one-time forces and set velocities. The component is automatically removed after application.
Forces are applied as impulses at the entity’s center of mass:
// Make object jump
entity.addComponent(PhysicsManipulation, {
force: [0, 10, 0], // Upward force
});
// Throw object forward
entity.addComponent(PhysicsManipulation, {
force: [5, 2, 0], // Forward and up
});
Directly set linear and angular velocities:
// Set specific movement
entity.addComponent(PhysicsManipulation, {
linearVelocity: [3, 0, 0], // Move right at 3 m/s
angularVelocity: [0, 2, 0], // Spin around Y-axis
});
You can combine forces and velocities in a single manipulation:
entity.addComponent(PhysicsManipulation, {
force: [0, 5, 0], // Push up
linearVelocity: [2, 0, 0], // Set rightward velocity
angularVelocity: [0, 1, 0], // Add spin
});
Creating a Falling Object
const box = new Mesh(new BoxGeometry(1, 1, 1), new MeshStandardMaterial());
box.position.set(0, 10, 0);
scene.add(box);
const entity = world.createTransformEntity(box);
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto,
});
entity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic,
});
Creating a Static Environment
const floor = new Mesh(new PlaneGeometry(20, 20), new MeshStandardMaterial());
floor.position.set(0, 0, 0);
scene.add(floor);
const floorEntity = world.createTransformEntity(floor);
floorEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto,
});
floorEntity.addComponent(PhysicsBody, {
state: PhysicsState.Static,
});
Interactive Grabbable Objects
// Create a grabbable ball
const ball = new Mesh(
new SphereGeometry(0.2),
new MeshStandardMaterial({ color: 0x00ff00 }),
);
ball.position.set(0, 2, 0);
scene.add(ball);
const ballEntity = world.createTransformEntity(ball);
ballEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.2],
density: 0.5,
restitution: 0.6,
});
ballEntity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic,
});
// Add grabbing capability (requires GrabSystem)
ballEntity.addComponent(Interactable);
ballEntity.addComponent(OneHandGrabbable);
const platform = new Mesh(
new BoxGeometry(3, 0.2, 3),
new MeshStandardMaterial({ color: 0x0000ff }),
);
scene.add(platform);
const platformEntity = world.createTransformEntity(platform);
platformEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [3, 0.2, 3],
friction: 0.8,
});
platformEntity.addComponent(PhysicsBody, {
state: PhysicsState.Kinematic,
});
// Animate the platform (in your update loop)
function animatePlatform(time) {
platform.position.y = Math.sin(time * 0.001) * 2 + 2;
}
- Use appropriate shape types:
- Primitives (Sphere, Box) are fastest
- ConvexHull for moderate complexity
- TriMesh only for static, highly detailed objects
- Optimize shape complexity:
- Simplify meshes when possible. Use ConvexHull over TriMesh when possble.
- Use fewer vertices for ConvexHull and TriMesh shapes
- Manage entity count:
- Remove physics components from distant objects
- Use object pooling for frequently created/destroyed objects
Physics objects fall through the floor:
- Ensure your floor has a
PhysicsShape component - Make sure floor has a
PhysicsBody component with PhysicsState.Static - Check that floor dimensions match visual geometry
Objects don’t collide:
- Verify both objects have
PhysicsShape components - Check that shapes have appropriate dimensions
- Ensure objects are not starting inside each other
- Make sure floor has a
PhysicsBody component with PhysicsState.Dynamic
Poor performance:
- Use simpler shape types when possible
- Reduce the number of physics entities
- Consider using TriMesh only for static objects
Objects behave unexpectedly:
- Check density values (very high/low values can cause issues)
- Verify friction and restitution are in reasonable ranges (0-1)
- Ensure forces aren’t too large
- Log component values to verify they’re set correctly
- Test with simple primitive shapes before using complex geometry
- Check console for warnings from the physics system
The physics components are also available in MetaSpatial projects through XML component definitions:
<!-- PhysicsBody component -->
<IWSDKPhysicsBody state="DYNAMIC" />
<!-- PhysicsShape component -->
<IWSDKPhysicsShape
shape="Box"
dimensions="2f, 1f, 1f"
density="1f"
friction="0.5f"
restitution="0f" />
<!-- PhysicsManipulation component -->
<IWSDKPhysicsManipulation
force="0f, 10f, 0f"
linearVelocity="0f, 0f, 0f"
angularVelocity="0f, 0f, 0f" />
Check out the examples/physics project in the SDK for a complete working example that demonstrates:
- Basic physics setup
- Dynamic sphere creation
- Force application
- Integration with XR interactions
The example creates a bouncing sphere with applied forces and can be run with:
cd examples/physics
pnpm install
pnpm dev