Calculate Impact Point Between Bodies in Matter.js

This article explains how to determine the precise point of impact between colliding rigid bodies using the Matter.js 2D physics engine. Matter.js detects collisions using the Separating Axis Theorem (SAT) and generates contact manifolds containing the vertices where bodies intersect. By capturing collision events, extracting support points, and calculating their geometric midpoint or using raycasting for continuous collision detection, you can accurately pinpoint where two bodies collide.

1. Listen for Collision Events

Matter.js provides collision lifecycle events through Matter.Events. The collisionStart event fires during the initial frame in which two bodies intersect, making it the ideal place to detect an impact.

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

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

        const impactPoint = getImpactPoint(pair);
        console.log('Impact coordinates:', impactPoint);
    }
});

2. Extract Collision Support Points

Each collision pair contains a collision object holding geometric information about the overlap. The pair.collision.supports array contains the specific vertices (support points) from the bodies that are actively involved in the penetration.

Depending on the collision geometry, supports typically contains one or two vertices:

3. Compute the Single Impact Coordinate

When two support points exist, the exact point of impact is commonly resolved as the midpoint between them. If only one support point exists, that point represents the impact location directly.

function getImpactPoint(pair) {
    const supports = pair.collision.supports;

    if (!supports || supports.length === 0) {
        // Fallback: Midpoint between body centroids if supports are unavailable
        return {
            x: (pair.bodyA.position.x + pair.bodyB.position.x) / 2,
            y: (pair.bodyA.position.y + pair.bodyB.position.y) / 2
        };
    }

    if (supports.length === 1) {
        return {
            x: supports[0].x,
            y: supports[0].y
        };
    }

    // Midpoint between the two support points
    return {
        x: (supports[0].x + supports[1].x) / 2,
        y: (supports[0].y + supports[1].y) / 2
    };
}

4. Account for Penetration Depth (High Precision)

Matter.js uses discrete collision detection, meaning bodies overlap slightly before the collision resolution separates them. If you need the point on the surface of bodyA rather than inside the overlapping region, offset the support point by half the collision penetration depth along the collision normal:

function getExactSurfaceImpactPoint(pair) {
    const collision = pair.collision;
    const supports = collision.supports;
    const normal = collision.normal; // Vector pointing from bodyA to bodyB
    const depth = collision.depth;

    let baseX, baseY;

    if (supports.length > 1) {
        baseX = (supports[0].x + supports[1].x) / 2;
        baseY = (supports[0].y + supports[1].y) / 2;
    } else {
        baseX = supports[0].x;
        baseY = supports[0].y;
    }

    // Adjust position along the normal to find the exact boundary
    return {
        x: baseX - normal.x * (depth * 0.5),
        y: baseY - normal.y * (depth * 0.5)
    };
}

5. Alternative: Raycasting for Fast-Moving Bodies

For fast-moving objects subject to tunneling (passing through other bodies between frames), discrete contact supports may reflect significant overlap. You can determine the pre-impact surface contact using Matter.Query.ray:

// Cast a ray from the previous position to the current position of bodyA
const rayHits = Matter.Query.ray([bodyB], bodyA.positionPrev, bodyA.position);

if (rayHits.length > 0) {
    const firstHit = rayHits[0];
    const exactImpact = {
        x: firstHit.point.x,
        y: firstHit.point.y
    };
}

Using pair.collision.supports handles standard collision responses, while combining it with penetration depth correction or raycasting ensures sub-frame accuracy for particle spawning, visual effects, and gameplay mechanics.