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) !== 0Both 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 = 0101If 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
}
};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.
- Condition 1:
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.
- Condition 1: