Modifying Matter.js Bodies on collisionStart
Yes, you can modify body properties during a
collisionStart event in Matter.js, but the method you use
depends heavily on which properties you change. While superficial
properties, forces, and velocities can often be adjusted immediately,
structural alterations—such as removing a body, adding constraints, or
toggling static states—can disrupt the internal collision solver and
cause simulation errors. This guide covers what you can safely alter
immediately and how to defer critical changes until the physics step
completes.
Safe Direct Modifications
During the collisionStart event, Matter.js has detected
overlapping pairs and is preparing to resolve (or has just begun
resolving) the collision response. You can directly update
non-structural properties without breaking the engine pipeline:
- Custom Properties: Setting game-state flags, hit
points, or custom metadata on
body.pluginor custom attributes. - Velocities and Impulses: Applying sudden forces
with
Matter.Body.applyForceor overriding velocity viaMatter.Body.setVelocity. - Sensors and Visuals: Toggling
body.isSensor = trueto allow objects to pass through each other after contact, or modifying rendering properties (e.g.,body.render.fillStyle).
Matter.Events.on(engine, 'collisionStart', (event) => {
event.pairs.forEach((pair) => {
const { bodyA, bodyB } = pair;
// Directly modify velocity or custom flags
if (bodyA.label === 'player') {
Matter.Body.setVelocity(bodyA, { x: 0, y: -10 });
bodyA.render.fillStyle = '#ff0000';
}
});
});Problematic
Modifications During collisionStart
Attempting to change structural or collision-mesh definitions during
the collision callback can crash the engine or lead to "ghost"
collisions. Avoid the following actions directly inside
collisionStart:
- Removing bodies via
Matter.Composite.remove. - Changing
body.isStatic(which shifts the body between the static and broadphase dynamic trees). - Scaling shapes (
Matter.Body.scale) or changing vertices directly. - Adding or removing constraints.
If you attempt to delete a body while Matter.js is still iterating through collision pairs involving that body, the solver may attempt to read undefined properties on subsequent iterations.
The Solution: Deferred Execution
To safely perform structural modifications triggered by a collision,
defer them until the current engine step concludes. You can achieve this
cleanly by listening to the afterUpdate event or using a
modification queue.
// A queue to hold changes needed after the physics step
const deferredTasks = [];
Matter.Events.on(engine, 'collisionStart', (event) => {
event.pairs.forEach((pair) => {
if (pair.bodyA.label === 'bullet') {
// Queue destructive or structural changes
deferredTasks.push(() => {
Matter.Composite.remove(engine.world, pair.bodyA);
Matter.Body.setStatic(pair.bodyB, true);
});
}
});
});
// Execute the queue once the solver finishes calculating the current step
Matter.Events.on(engine, 'afterUpdate', () => {
while (deferredTasks.length > 0) {
const task = deferredTasks.shift();
task();
}
});Using this pattern prevents race conditions, preserves the integrity of the broadphase collision tree, and ensures your simulation remains stable while reacting dynamically to collisions.