How to Check if a Body is Grounded in Matter.js

Determining whether a character is touching the ground is a fundamental requirement for 2D platformers and physics-based character controllers. In Matter.js, detecting ground contact can be handled through collision events, normal vector analysis, or dedicated sensor bodies. This article covers the most reliable methods to detect ground contact, focusing primarily on the compound sensor body pattern and collision normal checks to prevent unwanted behaviors like wall jumping.

The most robust technique for character controllers is attaching a non-physical "sensor" body to the bottom of your character's main physics body. Sensors fire collision events without applying physical forces or causing unwanted friction.

1. Create a Compound Body with a Sensor

const { Bodies, Body } = Matter;

const width = 40;
const height = 60;
const sensorHeight = 10;

// Main character body
const mainBody = Bodies.rectangle(x, y, width, height, {
    inertia: Infinity // Prevents character from tipping over
});

// Foot sensor positioned at the bottom of the main body
const sensor = Bodies.rectangle(x, y + height / 2, width * 0.8, sensorHeight, {
    isSensor: true,
    label: 'playerSensor'
});

// Combine into a compound body
const player = Body.create({
    parts: [mainBody, sensor],
    friction: 0.05
});

2. Track Ground State via Collision Events

Maintain an integer counter for active ground contacts. Increment the counter on collisionStart and decrement it on collisionEnd. Using a counter instead of a boolean prevents bugs when moving across multiple overlapping ground tiles.

let groundContactCount = 0;

Matter.Events.on(engine, 'collisionStart', (event) => {
    event.pairs.forEach((pair) => {
        if (pair.bodyA.label === 'playerSensor' || pair.bodyB.label === 'playerSensor') {
            groundContactCount++;
        }
    });
});

Matter.Events.on(engine, 'collisionEnd', (event) => {
    event.pairs.forEach((pair) => {
        if (pair.bodyA.label === 'playerSensor' || pair.bodyB.label === 'playerSensor') {
            groundContactCount = Math.max(0, groundContactCount - 1);
        }
    });
});

// Check if grounded before jumping
function isGrounded() {
    return groundContactCount > 0;
}

Method 2: Evaluating Collision Normals

If you prefer not to use a compound body, you can analyze collision normal vectors during collision events on a single body. This allows you to differentiate between hitting a wall, ceiling, or floor.

let isGrounded = false;

Matter.Events.on(engine, 'collisionActive', (event) => {
    let touchingGround = false;

    event.pairs.forEach((pair) => {
        // Ensure the player is involved in the collision
        if (pair.bodyA === player || pair.bodyB === player) {
            // Determine normal direction relative to the player
            const normal = pair.collision.normal;
            const isBodyA = pair.bodyA === player;
            
            // If player is bodyA, a normal pointing up (negative Y) means ground
            // If player is bodyB, the normal points in the opposite direction
            const normalY = isBodyA ? normal.y : -normal.y;

            // A normalY threshold of less than -0.5 confirms contact from below
            if (normalY < -0.5) {
                touchingGround = true;
            }
        }
    });

    isGrounded = touchingGround;
});

Method 3: Direct Queries with Matter.Query

For immediate checks outside of the event loop, use Matter.Query.ray or Matter.Query.collides. This approach checks if an area directly beneath the character intersects with other bodies right before applying a jump force.

function checkGroundDirectly(player, groundBodies) {
    const bottomCenter = {
        x: player.position.x,
        y: player.bounds.max.y
    };
    
    const rayEnd = {
        x: bottomCenter.x,
        y: bottomCenter.y + 5 // Check 5 pixels below the body
    };

    const collisions = Matter.Query.ray(groundBodies, bottomCenter, rayEnd);
    return collisions.length > 0;
}

Summary of Best Practices