Matter.js timeScale and Velocity Calculations Explained

This article explores how the timeScale property influences velocity calculations, displacement, and physics integration within Matter.js. It details the mathematical role of engine-level and body-level time scaling, explains how the engine's Verlet integration scheme derives velocity from positional changes, and covers how forces and damping adapt to time-step modifications.

In Matter.js, timeScale controls the rate at which time passes in the physics simulation. It can be applied globally via engine.timing.timeScale or on individual objects via body.timeScale. Both properties act as scalar multipliers that scale the effective integration step size (deltaTime) used across physics calculations.

Matter.js relies on a modified Verlet integration model rather than standard Euler integration. Instead of treating velocity as a fully decoupled variable that updates position in isolation, the engine inherently ties velocity to the displacement between a body's current position and its previous position (positionPrev). During each update tick, velocity is computed using the following relationship:

\[\text{velocity} = \frac{\text{position} - \text{positionPrev}}{\Delta t_{\text{normalised}}}\]

When timeScale is altered, it directly affects the normalized time delta passed into the integration phase. Specifically, the simulation scales the delta time:

\[\Delta t_{\text{effective}} = \Delta t \times \text{timeScale}\]

This adjustment affects velocity calculations in several specific ways:

1. Positional Integration and Effective Step Size

During Body.update, displacement per engine cycle is calculated by multiplying the current velocity by the scaled time delta. When timeScale decreases (e.g., set to 0.5 for slow motion), the displacement added to body.position in that step is halved. Because positionPrev retains the coordinate from the prior update, subsequent velocity derivations accurately reflect slower movement through world space.

2. Force and Acceleration Integration

External forces applied to a body (body.force) are converted into acceleration and added to the velocity vector during the integration cycle. Matter.js scales the force application by the normalized time step:

\[\Delta v_{\text{force}} = \left(\frac{\text{force}}{\text{mass}}\right) \times \Delta t_{\text{effective}}\]

If timeScale is reduced, the velocity increment gained from active forces in that specific tick decreases proportionally. This ensures that continuous forces, like global gravity, accelerate bodies consistently in simulated time rather than real time.

3. Damping and Air Resistance

Air resistance (body.frictionAir) is applied as a damping multiplier on previous displacement before computing the new velocity. The decay factor incorporates timeScale:

\[\text{frictionFactor} = 1 - (\text{body.frictionAir} \times \Delta t_{\text{effective}})\]

By scaling the friction factor with timeScale, Matter.js prevents drag from over-damping or under-damping bodies when the simulation speed changes. A object moving in slow motion retains its trajectory shape rather than stopping prematurely due to unscaled air resistance.

4. Global vs. Body-Specific timeScale

Modifying engine.timing.timeScale adjusts the delta globally across the collision detector, constraint solver, and all rigid bodies. In contrast, setting body.timeScale on a single body multiplies that specific body's local integration delta without affecting the rest of the engine. A body with a lower body.timeScale will update its positions and derived velocities slower than neighboring bodies, which can result in asymmetric collision responses when interacting with standard-speed objects.