Matter.js: Change Body Collision Filter at Runtime
Modifying body collision filters dynamically at runtime in Matter.js
is essential for mechanics such as passing through one-way platforms,
toggling invulnerability frames, or switching entity teams. This article
explains how Matter.js calculates collision rules, how to directly
mutate the collisionFilter properties on active physics
bodies during the simulation cycle, and how to correctly apply bitwise
masks for complex interactions.
Understanding
collisionFilter
Every Matter.js body has a collisionFilter object
containing three primary properties:
category: A 32-bit integer acting as a bitfield that defines what the body is. Default is0x0001.mask: A 32-bit integer bitmask defining which categories the body can collide with. Default is0xFFFFFFFF(collides with everything).group: A signed integer. If two colliding bodies share the same positive group value, they will always collide regardless of category and mask. If they share the same negative group value, they will never collide. Default is0.
Two bodies (Body A and Body B) will collide if and only if:
- Their
groupvalues are non-zero and identical (if positive, they collide; if negative, they do not). - If
groupis zero (or unequal), they collide when(BodyA.category & BodyB.mask) !== 0and(BodyB.category & BodyA.mask) !== 0.
Modifying the Filter at Runtime
To change collision behavior during the game loop or in response to
an event, directly update the properties of
body.collisionFilter. There is no need to remove and re-add
the body to the engine; Matter.js evaluates collision pairs on every
tick of the engine.
// Define custom categories as bitflags
const CATEGORY_DEFAULT = 0x0001;
const CATEGORY_PLAYER = 0x0002;
const CATEGORY_ENEMY = 0x0004;
const CATEGORY_PASSABLE = 0x0008;
// Example: Turn off collisions between a player and enemies (e.g., during a dash or invulnerability)
function enablePlayerGhostMode(playerBody) {
// Modify the mask to collide with DEFAULT, but exclude ENEMY
playerBody.collisionFilter.mask = CATEGORY_DEFAULT;
}
function disablePlayerGhostMode(playerBody) {
// Restore mask to collide with both DEFAULT and ENEMY
playerBody.collisionFilter.mask = CATEGORY_DEFAULT | CATEGORY_ENEMY;
}Using Bitwise Operators for Dynamic Changes
Bitwise operators make it straightforward to add or remove specific interaction layers on the fly without overwriting existing settings:
Add a collision target: Use the bitwise OR (
|) operator.body.collisionFilter.mask |= CATEGORY_ENEMY;Remove a collision target: Use the bitwise NOT (
~) and AND (&) operators.body.collisionFilter.mask &= ~CATEGORY_ENEMY;Toggle a collision target: Use the bitwise XOR (
^) operator.body.collisionFilter.mask ^= CATEGORY_ENEMY;
Instant Separation
with the group Property
If you need two bodies to immediately ignore each other without recomputing bitmasks, assign them matching negative group IDs at runtime:
// Prevent dynamicBodyA and dynamicBodyB from colliding
const uniqueGroupId = Matter.Body.nextGroup(true); // Generates a negative ID
dynamicBodyA.collisionFilter.group = uniqueGroupId;
dynamicBodyB.collisionFilter.group = uniqueGroupId;To re-enable collision rules based on category and mask, reset
collisionFilter.group to 0 on both bodies.