Stop Characters Sticking to Walls in Matter.js

When developing a 2D platformer in Matter.js, players often experience an unwanted effect where the character clings to vertical walls if a directional movement key is held mid-air. This sticking issue is caused by default physics friction and collision resolving between the player's body and vertical surfaces. This article covers why this occurs and details the most effective solutions, ranging from adjusting friction properties to building composite bodies with dedicated sensors.

Why Wall Sticking Occurs

By default, Matter.js rigid bodies are assigned non-zero values for friction and frictionStatic. When a player holds down a movement key against a vertical wall, horizontal force or velocity continuously pushes the player against the wall. The physics engine calculates normal contact force, and the resulting vertical friction counteracts gravity, keeping the character suspended mid-air until the directional key is released.

Solution 1: Zero Out Friction on the Player Body

The fastest way to eliminate wall sticking is to remove friction from the player body entirely.

const player = Bodies.rectangle(x, y, width, height, {
  friction: 0,
  frictionStatic: 0,
  frictionAir: 0.01,
  inertia: Infinity // Prevents character rotation
});

Because horizontal wall contact no longer produces friction, gravity pulls the player down smoothly regardless of whether lateral keys are pressed.

Note: Setting body friction to zero means the player will also slide across the floor without slowing down naturally. To handle ground movement properly, you must manually control deceleration in your update loop:

// Manual horizontal deceleration on the ground
if (!isMovingLeft && !isMovingRight && isGrounded) {
  Matter.Body.setVelocity(player, {
    x: player.velocity.x * 0.8,
    y: player.velocity.y
  });
}

A standard platformer architecture isolates wall interactions from floor interactions using a compound body. You create a frictionless main collider for the torso/walls and a separate sensor body at the base for ground detection.

const bodyWidth = 40;
const bodyHeight = 60;

// Main body that touches walls (frictionless)
const mainBody = Bodies.rectangle(x, y, bodyWidth, bodyHeight, {
  chamfer: { radius: 10 },
  friction: 0,
  frictionStatic: 0
});

// Ground sensor positioned slightly below the main body
const footSensor = Bodies.rectangle(x, y + bodyHeight / 2, bodyWidth * 0.8, 5, {
  isSensor: true,
  isStatic: false
});

// Combine into one compound body
const player = Body.create({
  parts: [mainBody, footSensor],
  inertia: Infinity,
  friction: 0,
  frictionStatic: 0
});

Using an isSensor body at the base lets you trigger collision events to toggle an isGrounded boolean. This enables grounded friction logic or direct velocity control while ensuring the main body never catches on walls.

Solution 3: Add Chamfer to Colliders

Sharp rectangular corners easily snag on the seams of tiled tilemaps and wall edges. Adding a chamfer to the player's collision box creates rounded corners that slide naturally over geometry transitions:

const player = Bodies.rectangle(x, y, width, height, {
  chamfer: { radius: 8 },
  friction: 0
});

Alternatively, you can approximate a capsule collider by stacking circles or combining a rectangle with rounded ends.

Solution 4: Zero Lateral Velocity on Wall Contact

If your game mechanics require walls to have friction for other objects, you can selectively disable horizontal input when touching a wall.

Use Matter.js collision events (collisionActive) to check contact normals:

Matter.Events.on(engine, 'collisionActive', (event) => {
  event.pairs.forEach((pair) => {
    if (pair.bodyA === player || pair.bodyB === player) {
      const collision = pair.collision;
      
      // Check if the collision normal is predominantly horizontal
      if (Math.abs(collision.normal.x) > 0.5) {
        // Stop the player from exerting inward force into the wall
        if ((movingRight && collision.normal.x < 0) || (movingLeft && collision.normal.x > 0)) {
          Matter.Body.setVelocity(player, { x: 0, y: player.velocity.y });
        }
      }
    }
  });
});

This prevents lateral acceleration against vertical walls while keeping your vertical velocity intact for smooth falling.