Top-Down Drift Car Physics in Matter.js

This guide explains how to implement realistic top-down drift vehicle physics in Matter.js by overriding default rigid-body behavior with custom tire friction. By decomposing the vehicle's velocity into forward and lateral vectors on every engine update, you can cancel sideways sliding during standard turns while selectively allowing controlled lateral slippage when initiating a drift or handbrake slide.

The Core Concept: Anisotropic Friction

Standard 2D physics engines like Matter.js treat rigid bodies with isotropic friction, meaning resistance is applied equally in all directions. A realistic car tire behaves anisotropically: it rolls freely forward and backward with minimal resistance, but strongly resists lateral (sideways) movement.

To achieve a drift mechanic:

  1. Decompose the car's velocity vector into forward and lateral components.
  2. Apply an opposing force or directly scale down the lateral velocity to simulate tire grip.
  3. Dynamically lower this lateral resistance to trigger a drift when turning sharply or applying a handbrake.

1. Initializing the Car Body

Create a basic rectangular rigid body to represent the chassis. Set ambient friction and air resistance low so that friction can be handled entirely via code in the update loop.

const car = Matter.Bodies.rectangle(400, 300, 40, 80, {
  density: 0.002,
  frictionAir: 0.02,
  restitution: 0.1,
  angle: 0
});

Matter.Composite.add(engine.world, car);

2. Vector Decomposition

On every update tick (beforeUpdate), calculate unit vectors corresponding to the vehicle's local orientation:

Next, calculate the dot product between the vehicle's actual linear velocity and these unit vectors to separate speed into forward and lateral magnitudes:

Matter.Events.on(engine, 'beforeUpdate', () => {
  const angle = car.angle;
  
  // Direction vectors
  const forwardVector = { x: Math.sin(angle), y: -Math.cos(angle) };
  const rightVector = { x: Math.cos(angle), y: Math.sin(angle) };

  // Project velocity onto directional axes
  const forwardVelocity = car.velocity.x * forwardVector.x + car.velocity.y * forwardVector.y;
  const lateralVelocity = car.velocity.x * rightVector.x + car.velocity.y * rightVector.y;

3. Applying Lateral Friction and Drift Logic

To prevent slipping under normal driving, eliminate most of the lateral velocity. When drifting (such as holding a handbrake key or exceeding a lateral acceleration threshold), retain a fraction of that lateral velocity to allow sliding.

  // Adjust grip: lower value creates more slide
  let grip = 0.95; // Default grip (eliminates 95% of lateral speed per tick)

  if (keys.handbrake) {
    grip = 0.82; // Handbrake reduces lateral resistance
  } else if (Math.abs(lateralVelocity) > 4) {
    // Dynamic loss of traction under hard steering at high speed
    grip = 0.88;
  }

  // Calculate the corrected lateral velocity
  const correctedLateral = lateralVelocity * (1 - grip);

  // Reconstruct velocity: preserve forward speed, reduce lateral speed
  const newVelocity = {
    x: forwardVector.x * forwardVelocity + rightVector.x * correctedLateral,
    y: forwardVector.y * forwardVelocity + rightVector.y * correctedLateral
  };

  Matter.Body.setVelocity(car, newVelocity);

4. Drive, Reverse, and Steering Controls

Apply forces along the forward vector for propulsion and adjust angular velocity or angle for steering. Steering authority should scale with forward velocity so the vehicle does not pivot in place when stationary.

  const enginePower = 0.005;
  const steerSpeed = 0.04;

  // Throttle and Reverse
  if (keys.up) {
    Matter.Body.applyForce(car, car.position, {
      x: forwardVector.x * enginePower,
      y: forwardVector.y * enginePower
    });
  } else if (keys.down) {
    Matter.Body.applyForce(car, car.position, {
      x: -forwardVector.x * (enginePower * 0.5),
      y: -forwardVector.y * (enginePower * 0.5)
    });
  }

  // Steering (only allowed if vehicle is moving)
  const speedFactor = Math.min(Math.abs(forwardVelocity) / 5, 1);
  const directionMultiplier = forwardVelocity < 0 ? -1 : 1;

  if (keys.left) {
    Matter.Body.setAngularVelocity(car, -steerSpeed * speedFactor * directionMultiplier);
  } else if (keys.right) {
    Matter.Body.setAngularVelocity(car, steerSpeed * speedFactor * directionMultiplier);
  } else {
    // Natural angular dampening to settle the car
    Matter.Body.setAngularVelocity(car, car.angularVelocity * 0.8);
  }
});

5. Fine-Tuning Drift Physics