How to Listen for Engine Events in Matter.js

Matter.js provides a built-in event system that allows developers to hook into the simulation lifecycle, monitor state updates, and detect physical interactions. This guide covers how to use the Matter.Events module to bind callbacks to the Matter.js engine, detailing the most common engine events, their practical applications, and clear code examples for implementation.

The Matter.Events.on Method

To listen for events emitted by an engine instance, use the Matter.Events.on function. This method registers a callback function that triggers whenever the specified event occurs.

Matter.Events.on(engine, 'eventName', function(event) {
    // Logic executed on event trigger
});

The first argument is the target object emitting the event (the engine), the second is a string representing the event name, and the third is the callback function, which receives an event object containing contextual data.

Essential Engine Events

The Matter.js Engine emits several lifecycle and collision events throughout its update loop.

1. beforeUpdate

Fires just before the engine runs an update step. This is useful for applying custom forces, modifying velocities, or adjusting body properties before physics calculations occur.

Matter.Events.on(engine, 'beforeUpdate', function(event) {
    // Apply a custom force to a body before the step calculates
    Matter.Body.applyForce(myBody, myBody.position, { x: 0, y: -0.05 });
});

2. afterUpdate

Fires immediately after the engine finishes an update step. Use this to synchronize external renderers, update non-physics game state, or read updated body coordinates.

Matter.Events.on(engine, 'afterUpdate', function(event) {
    // Read the updated coordinates after physics calculations
    console.log('Body position:', myBody.position.x, myBody.position.y);
});

3. collisionStart

Fires when two or more bodies begin to collide during an update step. The event object includes a pairs array containing the collision details.

Matter.Events.on(engine, 'collisionStart', function(event) {
    const pairs = event.pairs;

    for (let i = 0; i < pairs.length; i++) {
        const bodyA = pairs[i].bodyA;
        const bodyB = pairs[i].bodyB;

        console.log('Collision started between:', bodyA.label, bodyB.label);
    }
});

4. collisionActive

Fires during every update tick where two bodies remain in contact after the initial collision step.

Matter.Events.on(engine, 'collisionActive', function(event) {
    event.pairs.forEach(pair => {
        // Continuous contact handling (e.g., surface friction damage)
    });
});

5. collisionEnd

Fires when two bodies separate and the collision state terminates.

Matter.Events.on(engine, 'collisionEnd', function(event) {
    event.pairs.forEach(pair => {
        console.log('Collision ended between:', pair.bodyA.label, pair.bodyB.label);
    });
});

Unsubscribing from Events

To remove an active listener, use Matter.Events.off. You must pass the target, the event name, and the exact reference to the callback function that was registered.

function onUpdate(event) {
    console.log('Engine updated');
}

// Subscribe
Matter.Events.on(engine, 'afterUpdate', onUpdate);

// Unsubscribe
Matter.Events.off(engine, 'afterUpdate', onUpdate);

Passing only the target and the event name to Matter.Events.off(engine, 'afterUpdate') will unbind all listeners associated with that specific event.