Advanced collision-detection

Collision-detection is a two-steps process. First the broad-phase detects pairs of colliders that are potentially in contact or intersecting. Second, the narrow-phase processes all these pairs in order to compute contacts points and generate contact or intersection events.

All the pairs detected by the broad-phase can be accessed from the physics World class.

Contact and intersection events#

The narrow-phase can generate two types of events: contact events between two non-sensor colliders or intersection events between one sensor collider and another collider.

There events can be collected by optionally passing an instance of EventQueue to World.step(eventQueue). The colliders involved in each event is identified by its integer handle.

let eventQueue = new RAPIER.EventQueue(true);
eventQueue.drainContactEvents((handle1, handle2, started) => {
/* Handle the contact event. */
eventQueue.drainIntersectionEvents((handle1, handle2, intersecting) => {
/* Handle the intersection event. */

Intersection events are triggered when two colliders (where at least one of them is a sensor) transition between intersecting or not. Contact events are triggered when two non-sensor colliders transition between having zero contact points and having at least one contact point. Transitioning between having one contact point to having more than one contact points is not reported an event.


Intersection (resp. contact) events are only generated between two colliders (where at least one of the is a sensor) if at least one of them has the ActiveEvents.INTERSECTION_EVENTS (resp. ActiveEvents.CONTACT_EVENTS) flag in its active events.

The contact graph#

The contact graph (encapsulated by the World class) can be read in order to determine whether two specific non-sensor colliders are in contact, or to determine all the non-sensor colliders in contact with one particular non-sensor collider. Contact points and contact normals will also be provided when a contact exists.

The contact geometry (contact points, contact normal, penetration depth, etc.) can be read from the contact manifolds stored in a contact pair:

  1. Each contact pair may contain multiple contact manifolds. Each contact manifold represents a set of contacts sharing the same contact normal.
  2. Each contact manifold contains the list of geometric contacts detected by the narrow-phase.
  3. Each contact manifold also contains a list of contacts that were processed by the constraints solver for force calculation (aka. the solver contacts). These solver contacts are a subset of the contacts detected by the narrow-phase, expressed in a way that is more efficient for the constraints solver to process.

All the geometric contact data are expressed in the local-space of the colliders. The solver contacts are expressed in world-space.


There will always be only up to one contact manifold between two colliders with convex primitive shapes. If one collider has a shape composed of several pieces (trimesh, polyline, heightfield, or compound shape) then there will be multiple contact manifolds, one for each piece that may result in an actual contact.

world.contactsWith(colliderHandle, (otherColliderHandle) => {
// This closure is called on the handle of each collider potentially
// in contact with `colliderHandle`.
world.contactPair(handle1, handle2, (manifold, flipped) => {
// Contact information can be read from `manifold`.

The intersection graph#

The intersection graph can be read in order to determine whether two specific colliders (assuming at least one of them is a sensor) are intersecting, or to determine all the colliders intersecting one particular collider (assuming at least one collider of each pair is a sensor). The intersection graph contains one graph edge for each pair of colliders such that:

  1. At least one of the collider is a sensor.
  2. And they are close enough so the broad-phase considers they have a chance to be intersecting.

Each such edge contains one boolean indicating if the colliders are actually intersecting or not:


Keep in mind that intersection tests are performed between two colliders only if at least one of the colliders is a sensor. If they are both non-sensor colliders then they will be involved in the contact graph instead of the intersection graph.

world.intersectionsWith(colliderHandle, (otherColliderHandle) => {
// This closure is called on the handle of each collider potentially
// intersecting the collider with handle `colliderHandle`.
let intersections = world.intersectionPair(handle1, handle2);

Physics hooks#

Physics hooks are user-defined callbacks used to change the behavior of the physics simulation. In particular, they can be used to filter contacts (in a more flexible way than collision groups and solver groups).

Contact and intersection filtering#

Sometimes, collision groups and solver groups are not flexible enough to achieve the desired behavior. In that case, the contact filtering hooks let you apply custom rules to filter contact pairs and intersection pairs:

  • For each potential contact pair (between two non-sensor colliders) detected by the broad-phase, if at least one of the colliders involved in the pair has the bit ActiveHooks.FILTER_CONTACT_PAIRS enabled in its active hooks, then PhysicsHooks.firterContactPair will be called. If this filter returns null then no contact computation will happen for this pair of colliders. If it returns a SolverFlags value then the narrow-phase will compute contact points.
  • For each potential intersection pair (between a sensor colliders and another collider) detected by the broad-phase, if at least one of the colliders involved in the pair has the bit ActiveHooks.FILTER_INTERSECTION_PAIRS enabled in its active hooks, then PhysicsHooks.filterIntersectionPair will be called. If this filter returns false then no intersection computation will happen for this pair of colliders. If it returns true then the narrow-phase will test whether or not they are intersecting.

When PhysicsHooks.filterContactPair returns a not-null value, it needs to provide a set of solver flags for this contact pair. These solver flags indicate what happen with the contacts of this contact pair afterwards:

  • If the returned solver flags contains the SolverFlags.COMPUTE_IMPULSES bit, then the constraints solver will compute forces for these contacts. If this bit is not included in the returned flags, then no contact force will be computed for this pair of colliders.

Right now there is no solver flags other than SolverFlags.COMPUTE_IMPULSES. Other flags may be added in the future.

Continuous Collision Detection#

Continuous Collision Detection (CCD) is used to make sure that fast-moving objects don't miss any contacts (a problem usually called tunneling). See the rigid-body CCD section for details.