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.