How to Model Anisotropic Friction in Matter.js

This article explains how to simulate anisotropic friction—resisting lateral sliding while allowing smooth forward motion—within the Matter.js 2D physics engine. Because Matter.js relies on standard, isotropic Coulomb friction by default, achieving directional friction requires intercepting the physics update loop. By decomposing a body's velocity into local longitudinal and lateral vectors, you can apply custom corrective impulses or forces to damp sideways drift, making it ideal for modeling vehicles, ice skates, or skis.

The Limitation of Native Friction

Matter.js bodies use scalar properties for friction: friction, frictionAir, and frictionStatic. These values apply resistance uniformly in all directions. To model objects like wheels or skate blades, the physics engine needs to treat the object's forward axis differently from its perpendicular (sideways) axis.

The Vector Decomposition Approach

To simulate anisotropic friction, hook into the beforeUpdate event of the Matter.js engine. The process involves three steps:

  1. Determine the body’s forward and perpendicular (lateral) unit vectors using its current rotation angle.
  2. Project the body's current linear velocity onto both vectors to separate forward speed from lateral drift.
  3. Apply an opposing force or directly scale down the lateral velocity component before the collision and position updates take place.

Implementation

Below is a complete implementation demonstrating how to damp lateral velocity on a rigid body:

const { Engine, Render, Runner, Bodies, Composite, Events, Vector } = Matter;

// Initialize engine and world
const engine = Engine.create();
const world = engine.world;

// Disable gravity for a top-down perspective
engine.gravity.y = 0;

// Create a body (e.g., a car or skate blade)
const vehicle = Bodies.rectangle(400, 300, 40, 80, {
    angle: 0,
    frictionAir: 0.01 // Low default air resistance
});

Composite.add(world, vehicle);

// Define friction coefficients
const lateralFriction = 0.90; // Higher value = less sideways sliding (0 to 1)
const forwardFriction = 0.01; // Longitudinal resistance

Events.on(engine, 'beforeUpdate', () => {
    // 1. Calculate local directional unit vectors
    const angle = vehicle.angle;
    
    // Forward vector (assuming the body's local forward points along its length)
    const forwardVector = {
        x: Math.sin(angle),
        y: -Math.cos(angle)
    };

    // Right (lateral) vector perpendicular to the forward vector
    const rightVector = {
        x: Math.cos(angle),
        y: Math.sin(angle)
    };

    // 2. Project current velocity onto both axes (dot product)
    const currentVelocity = vehicle.velocity;
    const forwardSpeed = Vector.dot(currentVelocity, forwardVector);
    const lateralSpeed = Vector.dot(currentVelocity, rightVector);

    // 3. Apply damping factors
    const newForwardSpeed = forwardSpeed * (1 - forwardFriction);
    const newLateralSpeed = lateralSpeed * (1 - lateralFriction);

    // Reconstruct the modified velocity vector
    const newVelocity = {
        x: (forwardVector.x * newForwardSpeed) + (rightVector.x * newLateralSpeed),
        y: (forwardVector.y * newForwardSpeed) + (rightVector.y * newLateralSpeed)
    };

    // Update the body's velocity directly
    Matter.Body.setVelocity(vehicle, newVelocity);
});

Tuning Drift and Grip