Matter.js Collision Filtering with Bitwise Operators

In Matter.js, collision filtering dictates which bodies collide and which pass through each other using binary arithmetic. This system relies on 32-bit integers and bitwise operators—primarily bitwise AND (&) and bitwise OR (|)—to evaluate matching flags between an object’s type and the targets it is allowed to interact with. By assigning unique power-of-two values to categories and combining them into masks, developers can define complex, performant interaction rules across numerous game objects with minimal computation.

The Collision Rule

Matter.js evaluates whether two bodies (bodyA and bodyB) collide by checking their collisionFilter properties. Unless overridden by a matching non-zero collisionFilter.group, a collision occurs if and only if both of the following conditions evaluate to true:

(bodyA.collisionFilter.category & bodyB.collisionFilter.mask) !== 0
&&
(bodyB.collisionFilter.category & bodyA.collisionFilter.mask) !== 0

Both objects must actively "agree" to collide. If either body's mask excludes the other's category, they will pass through each other.

Defining Categories Using Bit Shifts

A category represents what an object is. Because Matter.js uses 32-bit integers for filtering, you can define up to 32 distinct collision categories. Each category must occupy a single binary bit (a power of two).

You can define these easily using hexadecimal values or the bitwise left-shift operator (<<):

const CATEGORY_DEFAULT = 0x0001; // Binary: 0001 (or 1 << 0)
const CATEGORY_PLAYER  = 0x0002; // Binary: 0010 (or 1 << 1)
const CATEGORY_ENEMY   = 0x0004; // Binary: 0100 (or 1 << 2)
const CATEGORY_BULLET  = 0x0008; // Binary: 1000 (or 1 << 3)

Assigning values that are not powers of two (such as 3, which is binary 0011) will cause an object to belong to multiple categories simultaneously, which can lead to unexpected collision behavior.

Combining Targets Using Bitwise OR

A mask represents what an object collides with. To allow a body to collide with multiple categories, you combine those categories using the bitwise OR (|) operator.

The OR operator compares bits: if either bit is 1, the result is 1.

// This mask collides with both enemies and default environmental objects
const playerMask = CATEGORY_DEFAULT | CATEGORY_ENEMY;
// Binary: 0001 | 0100 = 0101

If you want an object to collide with everything, Matter.js uses a default mask of 0xFFFFFFFF (all bits set to 1). To make an object collide with nothing, set its mask to 0.

How the Bitwise AND Evaluates Collisions

When the physics engine checks two bodies, it applies the bitwise AND (&) operator to compare one body's category against the other's mask. The AND operator produces a 1 only if both bits are 1. If any overlap exists, the numerical result is non-zero (evaluating to true).

Step-by-Step Example

Consider a player's bullet and two potential collision partners:

const playerBullet = {
  collisionFilter: {
    category: CATEGORY_BULLET, // 1000
    mask: CATEGORY_ENEMY       // 0100
  }
};

const enemy = {
  collisionFilter: {
    category: CATEGORY_ENEMY,  // 0100
    mask: CATEGORY_DEFAULT | CATEGORY_BULLET // 0001 | 1000 = 1001
  }
};

const player = {
  collisionFilter: {
    category: CATEGORY_PLAYER, // 0010
    mask: CATEGORY_DEFAULT | CATEGORY_ENEMY  // 0001 | 0100 = 0101
  }
};
  1. Bullet vs. Enemy:

    • Condition 1: playerBullet.category & enemy.mask \(\rightarrow\) 1000 & 1001 = 1000 (8 \(\neq\) 0) \(\rightarrow\) True
    • Condition 2: enemy.category & playerBullet.mask \(\rightarrow\) 0100 & 0100 = 0100 (4 \(\neq\) 0) \(\rightarrow\) True
    • Result: The bullet and enemy collide.
  2. Bullet vs. Player:

    • Condition 1: playerBullet.category & player.mask \(\rightarrow\) 1000 & 0101 = 0000 (0) \(\rightarrow\) False
    • Result: Evaluation halts immediately; the bullet passes harmlessly through the player.