How to Use beforeUpdate in Matter.js
This article explains how to leverage the beforeUpdate
event in Matter.js to implement continuous custom physics and game
logic. By hooking into the physics engine immediately before every
simulation step, developers can reliably handle real-time mechanics such
as player input, custom forces, continuous trajectory adjustments, and
frame-by-frame constraint modifications.
Understanding the beforeUpdate Event
In Matter.js, the physics simulation runs on a discrete loop managed
by Matter.Engine. During each tick, the engine advances the
simulation, resolves collisions, and updates body positions and
velocities.
The beforeUpdate event fires right before the engine
computes these changes. Because it runs prior to collision detection and
position resolution, it is the safest and most effective lifecycle hook
for altering the physical state of bodies without causing visual jitter
or physics instability.
Registering the Event Listener
To listen for the beforeUpdate event, use the
Matter.Events.on method, passing your engine instance, the
event name, and a callback function.
const { Engine, Events } = Matter;
const engine = Engine.create();
Events.on(engine, 'beforeUpdate', (event) => {
// Custom logic runs every frame here
});The event object passed to the callback provides
contextual details, including event.timestamp, which
indicates the current simulation time.
Applying Continuous Forces and Velocity
The most common application of beforeUpdate is applying
persistent forces or manually regulating velocity. Because forces
applied via Matter.Body.applyForce are consumed in a single
simulation step, they must be continuously reapplied inside
beforeUpdate to simulate sustained effects like thrusters,
wind, or localized gravity.
const { Engine, Events, Body, Vector } = Matter;
const engine = Engine.create();
const rocket = Matter.Bodies.rectangle(400, 500, 40, 80);
Events.on(engine, 'beforeUpdate', (event) => {
// Apply an upward thrust continuously
const upwardForce = Vector.create(0, -0.005);
Body.applyForce(rocket, rocket.position, upwardForce);
// Limit maximum falling speed
const maxVelocityY = 10;
if (rocket.velocity.y > maxVelocityY) {
Body.setVelocity(rocket, {
x: rocket.velocity.x,
y: maxVelocityY
});
}
});Handling Continuous User Input
Handling continuous keyboard or gamepad controls inside standard DOM
events can result in variable execution rates that do not match the
physics update cycle. Using beforeUpdate synchronizes user
input directly with the simulation step.
const keys = {
ArrowLeft: false,
ArrowRight: false
};
window.addEventListener('keydown', (e) => { keys[e.code] = true; });
window.addEventListener('keyup', (e) => { keys[e.code] = false; });
Events.on(engine, 'beforeUpdate', () => {
const moveSpeed = 5;
if (keys.ArrowLeft) {
Body.setVelocity(playerBody, { x: -moveSpeed, y: playerBody.velocity.y });
}
if (keys.ArrowRight) {
Body.setVelocity(playerBody, { x: moveSpeed, y: playerBody.velocity.y });
}
});Implementing Dynamic Constraints and Tracking
You can use beforeUpdate to manually tether objects,
align camera viewports to a moving body, or make a body look toward a
specific target coordinate:
Events.on(engine, 'beforeUpdate', () => {
// Rotate body towards a target point (e.g., mouse position)
const angle = Math.atan2(
target.y - follower.position.y,
target.x - follower.position.x
);
Body.setAngle(follower, angle);
});Performance Best Practices
- Keep Logic Lightweight: The
beforeUpdatecallback executes every frame (typically 60 times per second). Avoid heavy computations, deep array iterations, or memory allocations inside this function. - Avoid Hard Position Overwrites: Prefer
Body.setVelocityorBody.applyForceover directly modifyingBody.setPosition, as abruptly moving bodies can break collision resolution and cause tunneling through other bodies. - Decouple Rendering: Use
beforeUpdatepurely for updating physical properties and state. Handle rendering updates inside theafterRenderevent or your custom rendering loop.