Simulate Slow Motion in Matter.js Using Time Scale

Simulating slow motion in Matter.js is straightforward using the built-in timeScale property of the physics engine. By adjusting this value, you can control the speed of the physics update loop without manually recalculating velocities or forces. This article covers how timeScale works, how to implement sudden and smooth transitions for slow-motion effects, and best practices to ensure simulation stability.

How timeScale Works in Matter.js

The Matter.js engine includes a timing configuration object at engine.timing. Within this object, timeScale acts as a global multiplier for the delta time used in each physics update step.

Basic Implementation

To initiate slow motion instantly, change the engine.timing.timeScale property directly on your active engine instance:

// Create your engine
const engine = Matter.Engine.create();

// Trigger slow motion (e.g., 25% speed)
function enableSlowMotion() {
    engine.timing.timeScale = 0.25;
}

// Return to normal speed
function resetNormalSpeed() {
    engine.timing.timeScale = 1.0;
}

When using Matter.Runner, the runner automatically honors engine.timing.timeScale and scales the simulation steps accordingly.

Creating Smooth "Bullet Time" Transitions

Instantly snapping to a low timeScale can feel jarring. To create a cinematic bullet-time effect, smoothly interpolate the timeScale between values over time using a standard update loop or a tweening library.

Linear Interpolation (Lerp) Example

let targetTimeScale = 1.0;

function setSlowMotion(active) {
    targetTimeScale = active ? 0.1 : 1.0;
}

// Inside your main game loop or a Matter.js 'beforeUpdate' event
Matter.Events.on(engine, 'beforeUpdate', () => {
    // Smoothly approach the target scale
    engine.timing.timeScale += (targetTimeScale - engine.timing.timeScale) * 0.05;
});

Best Practices and Considerations

  1. Avoid Tunneling at High Scales: Reducing timeScale (slow motion) generally increases collision accuracy because movements per tick become smaller. However, if you abruptly increase timeScale well above 1.0, fast-moving objects may pass through thin barriers (tunneling).
  2. Body-Specific Scaling: The engine.timing.timeScale property affects every dynamic body in the world simultaneously. If you need a single body to move in slow motion while others move normally, you must manually manipulate that body's velocity and force properties instead of changing the global engine timescale.
  3. Framerate Independence: When using custom game loops rather than Matter.Runner.run(), ensure you pass scaled delta values into Matter.Engine.update(engine, delta) to maintain consistent behavior across displays with varying refresh rates.