How to Prevent Sliding on Slopes in Matter.js

Preventing a character from unintentionally sliding down inclined surfaces is a common challenge in 2D physics engines like Matter.js. By default, gravity decomposes along the angle of a slope, creating a lateral force that overcomes default friction values. This article covers practical techniques to eliminate unwanted slope sliding, including dynamic friction switching, counteracting gravitational forces, and manually clamping velocity during idle states.

1. Dynamic Friction Adjustments

A standard character body usually requires low friction (friction: 0 or 0.001) to prevent getting stuck on vertical walls during movement. However, low friction causes immediate sliding on slopes. The simplest fix is to dynamically toggle friction properties depending on whether the player is actively moving or stationary.

When player input is zero and the character is grounded:

// When stationary on ground
Matter.Body.set(playerBody, {
    friction: 1,
    frictionStatic: 10
});

// When moving via player input
Matter.Body.set(playerBody, {
    friction: 0.001,
    frictionStatic: 0
});

Make sure the slope surface also has adequate friction (e.g., friction: 1, frictionStatic: 2) so the two surfaces create enough resistance to counter the slope component of gravity.

2. Zeroing Velocity on Idle

Friction-based solutions can sometimes cause jitter on steep slopes. A more reliable alternative is to cancel velocity directly when the character is on the ground and receiving no directional input.

Listen to the engine's beforeUpdate event:

Matter.Events.on(engine, 'beforeUpdate', () => {
    if (isGrounded && !isMovingInput) {
        // Halt sliding by locking horizontal and downward drift
        if (Math.abs(playerBody.velocity.x) < 0.1) {
            Matter.Body.setVelocity(playerBody, { 
                x: 0, 
                y: Math.min(0, playerBody.velocity.y) 
            });
        }
    }
});

To make this work effectively, you must track isGrounded using collision events (collisionStart, collisionActive, collisionEnd) via bottom sensors or raycasting.

3. Cancelling Gravity on the Character

If you control character movement purely via kinematic methods (setting velocity manually), you can negate the effect of the world's gravity on the player body specifically when grounded.

Matter.Events.on(engine, 'beforeUpdate', () => {
    if (isGrounded && !isMovingInput) {
        const gravity = engine.gravity;
        
        // Apply an equal and opposite force to global gravity
        Matter.Body.applyForce(playerBody, playerBody.position, {
            x: -gravity.x * gravity.scale * playerBody.mass,
            y: -gravity.y * gravity.scale * playerBody.mass
        });
        
        // Dampen residual movement
        Matter.Body.setVelocity(playerBody, { x: 0, y: 0 });
    }
});

4. Chamfered or Capsule Colliders

Square and rectangular bodies tend to snag or rotate on slope edges. Use a capsule (a rectangle with rounded ends using Matter.Bodies.rectangle with chamfer: { radius: ... }) to ensure uniform contact geometry. Always set inertia: Infinity on the character body to prevent the physics engine from rotating the player as it interacts with inclined surfaces.