Options
All
  • Public
  • Public/Protected
  • All
Menu

Getting Started

cannon-es is a lightweight and easy to use 3D physics engine for the web. It is inspired by the three.js' simple API, and based upon ammo.js and the Bullet physics engine.

The first thing to set up is our physics world, which will hold all of our physics bodies and step the simulation forward.

Let's create a world with Earth's gravity. Note that cannon.js uses SI units (metre, kilogram, second, etc.).

const world = new CANNON.World({
  gravity: new CANNON.Vec3(0, -9.82, 0), // m/s²
})

To step the simulation forward, we have to call world.step() each frame. As a first argument we pass the fixed timestep at which we want the simulation to run, 1 / 60 means 60fps. As a second argument, we pass the elapsed time since the last .step() call. This is used to keep the simulation at the same speed independently of the framerate, since requestAnimationFrame calls may vary on different devices or there might be performance issues. Read more about fixed simulation stepping here.

const timeStep = 1 / 60 // seconds
let lastCallTime
function animate() {
  requestAnimationFrame(animate)

  const time = performance.now() / 1000 // seconds
  if (!lastCallTime) {
    world.step(timeStep)
  } else {
    const dt = time - lastCallTime
    world.step(timeStep, dt)
  }
  lastCallTime = time
}
// Start the simulation loop
animate()

Rigid Bodies are the entities which will be simulated in the world, they can be simple shapes such as Sphere, Box, Plane, Cylinder, or more complex shapes such as ConvexPolyhedron, Particle, Heightfield, Trimesh.

Let's create a basic sphere body.

const radius = 1 // m
const sphereBody = new CANNON.Body({
  mass: 5, // kg
  shape: new CANNON.Sphere(radius),
})
sphereBody.position.set(0, 10, 0) // m
world.addBody(sphereBody)

As you can see we specified a mass property, the mass defines behaviour of the body when being affected by forces.

When bodies have a mass and are affected by forces, they're called Dynamic bodies. There are also Kinematic bodies which aren't affected by forces but can have a velocity and move around. The third type of bodies are Static bodies which can only be positioned in the world and aren't affected by forces nor velocity.

If you pass a mass of 0 to a body, that body is automatically flagged as a static body. You can also explicit the body type in the body options. Let's create a static ground for example.

const groundBody = new CANNON.Body({
  type: Body.STATIC,
  shape: new CANNON.Plane(),
})
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) // make it face up
world.addBody(groundBody)

Here are all the previous snippets combined in a fully working example.

import * as CANNON from 'cannon-es'

// Setup our physics world
const world = new CANNON.World({
  gravity: new CANNON.Vec3(0, -9.82, 0), // m/s²
})

// Create a sphere body
const radius = 1 // m
const sphereBody = new CANNON.Body({
  mass: 5, // kg
  shape: new CANNON.Sphere(radius),
})
sphereBody.position.set(0, 10, 0) // m
world.addBody(sphereBody)

// Create a static plane for the ground
const groundBody = new CANNON.Body({
  type: Body.STATIC, // can also be achieved by setting the mass to 0
  shape: new CANNON.Plane(),
})
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) // make it face up
world.addBody(groundBody)

// Start the simulation loop
const timeStep = 1 / 60 // seconds
let lastCallTime
function animate() {
  requestAnimationFrame(animate)

  const time = performance.now() / 1000 // seconds
  if (!lastCallTime) {
    world.step(timeStep)
  } else {
    const dt = time - lastCallTime
    world.step(timeStep, dt)
  }
  lastCallTime = time

  // the sphere y position shows the sphere falling
  console.log(`Sphere y position: ${sphereBody.position.y}`)
}
animate()

Note that cannon doesn't take care of rendering anything to the screen, it just computes the math of the simulation. To actually show something to the screen you have to use rendering libraries such as three.js. Let's see how we can achieve that.

First of all, you have to create the body's correspondent entity in three.js. For example here is how you create a sphere in three.js.

const radius = 1 // m
const geometry = new THREE.SphereGeometry(radius)
const material = new THREE.MeshNormalMaterial()
const sphereMesh = new THREE.Mesh(geometry, material)
scene.add(sphereMesh)

Then, you have to wire up the three.js mesh with the cannon.js body. To do that, you copy the positional and rotational data from the body to the mesh each frame after having stepped the world.

function animate() {
  requestAnimationFrame(animate)

  // world stepping...

  sphereMesh.position.copy(sphereBody.position)
  sphereMesh.quaternion.copy(sphereBody.quaternion)

  // three.js render...
}
animate()

You should now see a falling ball on the screen! Check out the basic three.js example for the full code.

That's it for the basic example, to learn more about all the different features, head over to the examples page!

Index

Type aliases

BodySleepState

BodySleepState: typeof BODY_SLEEP_STATES[keyof typeof BODY_SLEEP_STATES]

BodySleepState

BodyType

BodyType: typeof BODY_TYPES[keyof typeof BODY_TYPES]

BodyType

ConvexPolyhedronContactPoint

ConvexPolyhedronContactPoint: {}

ConvexPolyhedronContactPoint

Type declaration

RayMode

RayMode: typeof RAY_MODES[keyof typeof RAY_MODES]

RayMode

RayOptions

RayOptions: { callback?: RaycastCallback; checkCollisionResponse?: boolean; collisionFilterGroup?: number; collisionFilterMask?: number; from?: Vec3; mode?: RayMode; result?: RaycastResult; skipBackfaces?: boolean; to?: Vec3 }

RayOptions

Type declaration

  • Optional callback?: RaycastCallback

    callback

  • Optional checkCollisionResponse?: boolean

    Set to false if you don't want the Ray to take collisionResponse flags into account on bodies and shapes.

    default

    true

  • Optional collisionFilterGroup?: number

    collisionFilterGroup

    default

    -1

  • Optional collisionFilterMask?: number

    collisionFilterMask

    default

    -1

  • Optional from?: Vec3

    from

  • Optional mode?: RayMode

    mode

  • Optional result?: RaycastResult

    result

  • Optional skipBackfaces?: boolean

    If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.

    default

    false

  • Optional to?: Vec3

    to

ShapeType

ShapeType: typeof SHAPE_TYPES[keyof typeof SHAPE_TYPES]

ShapeType

Variables

Const BODY_SLEEP_STATES

BODY_SLEEP_STATES: { AWAKE: 0; SLEEPING: 2; SLEEPY: 1 } = ...

BODY_SLEEP_STATES

Type declaration

  • AWAKE: 0
  • SLEEPING: 2
  • SLEEPY: 1

Const BODY_TYPES

BODY_TYPES: { DYNAMIC: 1; KINEMATIC: 4; STATIC: 2 } = ...

BODY_TYPES

Type declaration

  • DYNAMIC: 1
  • KINEMATIC: 4
  • STATIC: 2

Const RAY_MODES

RAY_MODES: { ALL: 4; ANY: 2; CLOSEST: 1 } = ...

RAY_MODES

Type declaration

  • ALL: 4
  • ANY: 2
  • CLOSEST: 1

Const SHAPE_TYPES

SHAPE_TYPES: { BOX: 4; COMPOUND: 8; CONVEXPOLYHEDRON: 16; CYLINDER: 128; HEIGHTFIELD: 32; PARTICLE: 64; PLANE: 2; SPHERE: 1; TRIMESH: 256 } = ...

The available shape types.

Type declaration

  • BOX: 4
  • COMPOUND: 8
  • CONVEXPOLYHEDRON: 16
  • CYLINDER: 128
  • HEIGHTFIELD: 32
  • PARTICLE: 64
  • PLANE: 2
  • SPHERE: 1
  • TRIMESH: 256