Simulate ABS in Matter.js with Angular Velocity

This article explains how to simulate an Anti-lock Braking System (ABS) in the Matter.js 2D physics engine by dynamically monitoring wheel slip and modulating wheel angular velocity. By algorithmically preventing a wheel from coming to a complete rotational halt while the vehicle chassis is still moving, you can prevent sliding, maintain optimal tire friction, and simulate realistic vehicle dynamics directly in the browser.

The Mechanics of Wheel Slip

In real-world vehicle dynamics, braking performance depends on the slip ratio (\(S\)). Slip measures the difference between the linear speed of the vehicle body (\(v\)) and the tangential surface speed of the rotating wheel (\(\omega \times r\)):

\[S = \frac{v - (\omega \cdot r)}{v}\]

To simulate ABS, the control loop must detect when \(S\) exceeds a predefined threshold (typically between 0.15 and 0.20) and reduce or reverse the braking force applied to the wheel's angular velocity.

Setting Up the Vehicle in Matter.js

A standard vehicle setup consists of a main body (chassis) and circular bodies (wheels) attached via pin or spring constraints.

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

// Create chassis and wheel
const chassis = Bodies.rectangle(400, 300, 200, 40, { density: 0.002 });
const wheelRadius = 30;
const wheel = Bodies.circle(340, 330, wheelRadius, {
    friction: 0.9,
    density: 0.005
});

// Attach wheel with an axle constraint
const axle = Constraint.create({
    bodyA: chassis,
    pointA: { x: -60, y: 30 },
    bodyB: wheel,
    stiffness: 1,
    length: 0
});

Implementing the ABS Control Loop

To modulate wheel angular velocity, hook into Matter.js's beforeUpdate event. In this hook, calculate the current slip ratio, determine whether ABS should intervene, and override wheel.angularVelocity.

1. Calculating Slip

Calculate the forward velocity vector of the chassis relative to the wheel's rotational direction. For a 2D side-view simulation moving horizontally along the X-axis:

function getSlipRatio(chassis, wheel, wheelRadius) {
    const forwardSpeed = Math.abs(chassis.velocity.x);
    if (forwardSpeed < 0.1) return 0; // Ignore low speeds to prevent division by zero

    // Linear speed from rotation
    const rotationalLinearSpeed = Math.abs(wheel.angularVelocity * wheelRadius);

    // Calculate slip ratio
    return (forwardSpeed - rotationalLinearSpeed) / forwardSpeed;
}

2. Modulating Angular Velocity

When the user applies the brakes, standard mechanics reduce wheel.angularVelocity down to zero. With ABS enabled, if the calculated slip ratio passes the critical threshold, the system temporarily eases the deceleration or clamps the angular velocity to an optimal rolling speed.

let isBraking = false;
const MAX_SLIP_THRESHOLD = 0.20;
const OPTIMAL_SLIP_TARGET = 0.15;
const BRAKE_DECELERATION = 0.05; // Base braking torque effect

Events.on(engine, 'beforeUpdate', () => {
    if (!isBraking) return;

    const forwardSpeed = chassis.velocity.x;
    const absSpeed = Math.abs(forwardSpeed);

    // If the vehicle has practically stopped, bring the wheel to a complete rest
    if (absSpeed < 0.2) {
        Body.setAngularVelocity(wheel, 0);
        return;
    }

    const currentSlip = getSlipRatio(chassis, wheel, wheelRadius);

    if (currentSlip > MAX_SLIP_THRESHOLD) {
        // ABS Intervention: Release brake pressure to allow the wheel to speed back up
        // Target an angular velocity that keeps slip at the optimal threshold
        const targetRotationalSpeed = (absSpeed * (1 - OPTIMAL_SLIP_TARGET)) / wheelRadius;
        const sign = Math.sign(forwardSpeed);

        // Nudge angular velocity toward target to simulate hydraulic pulsing
        const correctedAngularVelocity = wheel.angularVelocity + (sign * targetRotationalSpeed - wheel.angularVelocity) * 0.3;
        Body.setAngularVelocity(wheel, correctedAngularVelocity);
    } else {
        // Normal Braking: Decrement angular velocity
        const sign = Math.sign(wheel.angularVelocity);
        let newAngularVelocity = wheel.angularVelocity - sign * BRAKE_DECELERATION;

        // Prevent counter-rotation while braking forward
        if (Math.sign(newAngularVelocity) !== sign) {
            newAngularVelocity = 0;
        }

        Body.setAngularVelocity(wheel, newAngularVelocity);
    }
});

Fine-Tuning Parameters for Realism