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:
- Decompose the car's velocity vector into forward and lateral components.
- Apply an opposing force or directly scale down the lateral velocity to simulate tire grip.
- 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:
- Forward Vector:
[sin(angle), -cos(angle)](assuming 0 radians points upward) - Lateral (Right) Vector:
[cos(angle), sin(angle)]
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
- Rotational Inertia: Increase
car.inertiaif the vehicle snaps back to straight too quickly, or reduce it if turning feels sluggish. - Dynamic Weight Transfer: Increase rear slip by
moving the center of mass forward using
Matter.Body.setCentre(car, { x: 0, y: -10 }, true). - Counter-Steering Sensitivity: Allow increased steering angles during a drift state to reward counter-steering without completely spinning out.