Simulating Friction on Inclined Planes in Matter.js

This article explains how to model and test friction coefficients on an inclined plane with variable angles using the Matter.js 2D physics engine. It covers the underlying physics of static and kinetic friction on slopes, walks through configuring the Matter.js engine and bodies, demonstrates how to dynamically change the plane's angle, and provides a complete implementation code snippet.

The Physics of the Inclined Plane

When an object rests on a ramp inclined at an angle \(\theta\), gravity pulls it down the slope with a force proportional to \(\sin(\theta)\), while normal force presses it against the surface proportional to \(\cos(\theta)\).

Sliding begins at the critical angle \(\theta_c\), defined by:

\[\tan(\theta_c) = \mu_s\]

Where \(\mu_s\) is the coefficient of static friction. Once the angle exceeds \(\theta_c\), static friction is overcome, and the block accelerates down the ramp governed by the coefficient of kinetic friction (\(\mu_k\)).

Key Matter.js Friction Properties

Matter.js models surface resistance using properties defined on rigid bodies:

Implementation Steps

1. Setup Engine and World

Initialize the core Matter.js modules:

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

const engine = Engine.create();
const world = engine.world;

const render = Render.create({
  element: document.body,
  engine: engine,
  options: {
    width: 800,
    height: 600,
    wireframes: false
  }
});

Render.run(render);
Runner.run(Runner.create(), engine);

2. Create the Plane and the Test Block

Create the ramp as a static body and the block as a dynamic body placed on top of it.

const rampWidth = 500;
const rampHeight = 20;
const rampX = 400;
const rampY = 350;

// Static ramp
const ramp = Bodies.rectangle(rampX, rampY, rampWidth, rampHeight, {
  isStatic: true,
  friction: 1.0,
  frictionStatic: 1.0,
  render: { fillStyle: '#555' }
});

// Dynamic block to test friction
const block = Bodies.rectangle(300, 200, 40, 40, {
  friction: 0.3,          // Kinetic friction
  frictionStatic: 0.5,    // Static friction (critical angle ~ 26.5°)
  restitution: 0.0,       // Prevent bouncing
  render: { fillStyle: '#e74c3c' }
});

Composite.add(world, [ramp, block]);

3. Adjusting the Angle Dynamically

To demonstrate variable angles, update the ramp's angle using Body.setAngle(). To prevent unnatural collisions when adjusting an already active simulation, reset the block position when the angle changes:

function setInclineAngle(degrees) {
  const radians = (degrees * Math.PI) / 180;

  // Rotate ramp around its center
  Body.setAngle(ramp, radians);

  // Calculate position along the rotated surface to reset the test block
  const offset = -150; // Distance from center up the ramp
  const spawnX = rampX + offset * Math.cos(radians) - (rampHeight / 2 + 20) * Math.sin(radians);
  const spawnY = rampY + offset * Math.sin(radians) + (rampHeight / 2 + 20) * Math.cos(radians);

  Body.setPosition(block, { x: spawnX, y: spawnY });
  Body.setAngle(block, radians);
  Body.setVelocity(block, { x: 0, y: 0 });
  Body.setAngularVelocity(block, 0);
}

4. Connecting User Controls

Hook the function up to an HTML range slider to let users continuously vary the angle and observe the threshold where static friction gives way to motion:

<label for="angleSlider">Ramp Angle: <span id="angleValue">0</span>°</label>
<input id="angleSlider" type="range" min="0" max="60" value="0" step="1">

<script>
  const slider = document.getElementById('angleSlider');
  const display = document.getElementById('angleValue');

  slider.addEventListener('input', (event) => {
    const angle = parseFloat(event.target.value);
    display.textContent = angle;
    setInclineAngle(angle);
  });
</script>

Observing the Results

With frictionStatic: 0.5, the block remains stationary until the slider passes approximately \(26.5^\circ\) (\(\arctan(0.5)\)). Beyond this angle, gravitational component down the slope exceeds the maximum static frictional force, causing the block to slide down the ramp under the influence of its kinetic friction coefficient (0.3).