Skip to content

BabylonJS - Physics Engine

To add realistic physical interactions to your Babylon.js scenes – such as collisions, gravity, bouncing, friction, and joint constraints – you need to integrate a physics engine. Babylon.js uses a plugin system, allowing you to choose from several popular physics engines.

The physics engine runs a separate simulation alongside the visual rendering. It calculates how objects should move and interact based on physical laws. Babylon.js then updates the positions and orientations of your visual meshes to match the physics simulation results each frame. This creates the illusion of physical behavior like balls bouncing, objects falling, or structures collapsing.

Babylon.js supports several physics engine plugins:

  • Havok: (Official Integration) A highly optimized, commercial-grade physics engine known for its performance and stability. Requires a separate subscription for commercial use but offers a free version for non-commercial projects and evaluation. Often the best choice for complex simulations.
  • Ammo.js (WASM): A direct JavaScript port of the popular Bullet physics engine, compiled to WebAssembly (WASM) for near-native performance. It’s a feature-rich, open-source option and a very common choice.
  • Cannon.js: A lightweight, pure JavaScript physics engine. Easier to set up initially but generally less performant and potentially less stable than Ammo.js or Havok for complex scenarios.
  • Oimo.js: Another pure JavaScript physics engine, similar in scope to Cannon.js. Also less performant than WASM/Havok options.

The choice depends on your project’s performance needs, complexity, licensing requirements, and familiarity.

  1. Include the Physics Engine Library: You need to include the JavaScript file for your chosen physics engine (e.g., ammo.js, cannon.js, oimo.js, or the Havok library) in your project, typically via CDN or by installing it via npm.

  2. Enable Physics in the Scene: You need to explicitly enable physics simulation for your scene, usually specifying gravity.

  3. Create Physics Impostors: For a mesh to participate in the physics simulation, it needs a PhysicsImpostor. The impostor is an invisible, simplified physics shape (like a box, sphere, cylinder, plane, or mesh approximation) that represents the visual mesh in the physics world. You define the impostor type, mass, friction, restitution (bounciness), and other physical properties.

// 1. Include Ammo.js (e.g., via CDN in HTML or import if using npm)
// <script src="https://cdn.babylonjs.com/ammo.js"></script>
// OR if using npm/bundler:
// import Ammo from 'ammo.js'; // Adjust path based on your setup
// --- Inside your async scene creation function ---
// Ensure Ammo is loaded (it loads asynchronously)
await Ammo(); // Waits for the WASM module to be ready
// 2. Enable Physics using the AmmoJSPlugin
const gravityVector = new BABYLON.Vector3(0, -9.81, 0);
const physicsPlugin = new BABYLON.AmmoJSPlugin(); // Pass true for worker thread (optional)
scene.enablePhysics(gravityVector, physicsPlugin);
// 3. Create Impostors for meshes
// Example: Static Ground Impostor (mass = 0)
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 50, height: 50}, scene);
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
ground,
BABYLON.PhysicsImpostor.BoxImpostor, // Represent ground as a thin box
{ mass: 0, restitution: 0.9, friction: 0.5 },
scene
);
// Example: Dynamic Sphere Impostor (mass > 0)
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2}, scene);
sphere.position = new BABYLON.Vector3(0, 10, 0); // Start above ground
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
sphere,
BABYLON.PhysicsImpostor.SphereImpostor, // Use sphere shape for physics
{ mass: 1, restitution: 0.7, friction: 0.4 }, // Bouncy, some friction
scene
);

Impostor Types (BABYLON.PhysicsImpostor.*)

Section titled “Impostor Types (BABYLON.PhysicsImpostor.*)”
  • SphereImpostor
  • BoxImpostor
  • PlaneImpostor
  • CylinderImpostor
  • CapsuleImpostor (Plugin dependent, e.g., Ammo/Havok)
  • MeshImpostor (Approximates the mesh shape - more expensive)
  • HeightmapImpostor (For terrain)
  • NoImpostor (To remove physics from an object)
  • mass (number): Mass in kilograms. mass: 0 creates a static object (like ground or walls) that doesn’t move but participates in collisions.
  • restitution (number): Bounciness (0 = no bounce, 1 = fully elastic bounce).
  • friction (number): Resistance to sliding motion against other objects (0 = no friction).

You can interact with dynamic physics objects by applying forces or impulses:

  • applyForce(forceVector, contactPoint): Applies a continuous force (like pushing) at a specific point.
  • applyImpulse(impulseVector, contactPoint): Applies an instantaneous change in momentum (like hitting) at a specific point.
  • setLinearVelocity(velocityVector): Directly sets the object’s movement speed and direction.
  • setAngularVelocity(angularVelocityVector): Directly sets the object’s rotational speed.
// Example: Apply an upward impulse to the sphere when clicked
sphere.actionManager = new BABYLON.ActionManager(scene);
sphere.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, () => {
if (sphere.physicsImpostor) {
const impulse = new BABYLON.Vector3(0, 10, 0); // Upward impulse
const contactPoint = sphere.getAbsolutePosition(); // Apply at center
sphere.physicsImpostor.applyImpulse(impulse, contactPoint);
}
})
);

This demo creates multiple spheres falling onto a ground plane, interacting with each other using the Ammo.js physics engine.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Babylon.js - Physics Demo (Ammo.js)</title>
<!-- Make sure Ammo.js is loaded *before* Babylon.js if using separate scripts -->
<script src="https://cdn.babylonjs.com/ammo.js"></script>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<style>
html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; }
#renderCanvas { width: 100%; height: 100%; touch-action: none; }
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script type="text/javascript">
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const createScene = async function () {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.15, 1.0);
const camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 3, 40, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
camera.wheelPrecision = 50;
const light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
const pointLight = new BABYLON.PointLight("point", new BABYLON.Vector3(10, 10, -10), scene);
pointLight.intensity = 0.5;
// --- Physics Setup (Ammo.js) ---
try {
await Ammo(); // Wait for Ammo to initialize
console.log("Ammo.js loaded.");
const gravityVector = new BABYLON.Vector3(0, -9.81, 0);
const physicsPlugin = new BABYLON.AmmoJSPlugin();
scene.enablePhysics(gravityVector, physicsPlugin);
console.log("Physics enabled.");
} catch (e) {
console.error("Ammo.js failed to load or physics failed to enable:", e);
// Handle error appropriately, maybe show a message to the user
return scene; // Return scene without physics if it fails
}
// --------------------------------
// Ground
const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 30, height: 30 }, scene);
const groundMat = new BABYLON.StandardMaterial("groundMat", scene);
groundMat.diffuseColor = new BABYLON.Color3(0.5, 0.6, 0.5);
ground.material = groundMat;
ground.physicsImpostor = new BABYLON.PhysicsImpostor(ground, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 0, restitution: 0.8, friction: 0.5 }, scene);
// Create multiple spheres
const sphereRadius = 1;
const sphereMat = new BABYLON.StandardMaterial("sphereMat", scene);
sphereMat.diffuseColor = new BABYLON.Color3(0.9, 0.4, 0.4); // Reddish
sphereMat.specularColor = new BABYLON.Color3(0.2, 0.2, 0.2);
for (let i = 0; i < 20; i++) {
const sphere = BABYLON.MeshBuilder.CreateSphere(`sphere${i}`, { diameter: sphereRadius * 2 }, scene);
sphere.material = sphereMat;
// Stagger initial positions
sphere.position = new BABYLON.Vector3(
Math.random() * 10 - 5,
10 + i * 3,
Math.random() * 10 - 5
);
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
sphere,
BABYLON.PhysicsImpostor.SphereImpostor,
{ mass: 1, restitution: 0.6 + Math.random() * 0.3, friction: 0.4 }, // Slightly random bounciness
scene
);
}
return scene;
};
createScene().then(sceneToRender => {
if (!sceneToRender) return;
engine.runRenderLoop(function () {
sceneToRender.render();
});
window.addEventListener("resize", function () {
engine.resize();
});
}).catch(error => {
console.error("Scene creation failed:", error);
});
</script>
</body>
</html>

If Ammo.js loads correctly, this demo shows about 20 reddish spheres appearing at staggered heights above a green ground plane. Due to gravity enabled by the physics engine, the spheres fall, bounce off the ground (with slightly varying bounciness), collide with each other realistically, and eventually come to rest. If Ammo.js fails to load, the spheres would likely remain static in their initial positions (depending on error handling).

  • Performance: Physics simulations can be computationally intensive. Keep impostor shapes as simple as possible (Sphere, Box are cheaper than Mesh). Avoid large numbers of dynamic objects if performance is critical.
  • Async Loading: WASM-based engines like Ammo.js load asynchronously. Ensure the engine is loaded (await Ammo()) before enabling physics or creating impostors.
  • Units: Be consistent with units. Babylon.js typically uses meters.
  • Debugging: The Babylon.js Inspector has physics viewers (for some plugins) that can show the invisible impostor shapes, which is very helpful for debugging collision issues.