Fix Matter.js Bodies Passing Through Walls
When physics bodies move at high velocities in Matter.js, they often pass through solid obstacles—a physics engine phenomenon known as "tunneling." This occurs because Matter.js utilizes discrete collision detection, checking for intersections only at specific time intervals rather than continuously along a body's trajectory. If a body moves further than the thickness of a wall in a single frame, the engine never detects an overlap. Resolving this issue requires techniques such as increasing engine iterations, expanding wall thickness, clamping velocities, substepping the physics loop, or using raycasting.
Increase Wall Thickness
The simplest and most performant fix is to make your boundary bodies significantly thicker. If a wall is thin (e.g., 5 to 10 pixels), a fast-moving object can easily skip past it within one simulation step.
Extend the thickness of static walls outward, away from the visible play area. For example, give walls a thickness of 100 pixels or more while positioning them so only their inner surface aligns with the screen edge. The visual rendering can remain thin, but the collision body itself should be substantial.
Increase Engine Iterations
Matter.js provides settings to improve calculation precision at the cost of some performance:
engine.positionIterations = 10; // Default is 6
engine.velocityIterations = 8; // Default is 4Higher iteration values allow the solver to resolve overlaps more accurately during constraint and contact phases, reducing soft collisions and boundary sinking.
Implement Substepping (Smaller Time Steps)
By default, the physics runner updates at a fixed rate (usually 60Hz). If objects are moving extremely fast, updating the engine more frequently with smaller delta steps reduces the distance traveled per frame.
Instead of running a single update per animation frame, run multiple substeps:
const substeps = 4;
const delta = 1000 / 60 / substeps;
function gameLoop() {
for (let i = 0; i < substeps; i++) {
Matter.Engine.update(engine, delta);
}
requestAnimationFrame(gameLoop);
}This ensures that the distance a body travels between collision checks remains smaller than the thickness of the obstacles it encounters.
Clamp Maximum Velocity
If your application allows it, prevent bodies from reaching speeds
that exceed their own bounds or the thickness of the barriers. You can
clamp the velocity directly inside an update or
beforeUpdate event:
Matter.Events.on(engine, 'beforeUpdate', () => {
const maxSpeed = 20; // Adjust based on your thinnest wall
const speed = Matter.Vector.magnitude(body.velocity);
if (speed > maxSpeed) {
Matter.Body.setVelocity(body, {
x: (body.velocity.x / speed) * maxSpeed,
y: (body.velocity.y / speed) * maxSpeed
});
}
});Use Raycasting for Fast Projectiles
For extremely fast objects like bullets, discrete physics checks will frequently fail regardless of configuration. In these scenarios, avoid relying on physics bodies for collision detection.
Instead, perform a raycast along the trajectory using
Matter.Query.ray() before advancing the projectile:
const startPoint = body.position;
const endPoint = {
x: body.position.x + body.velocity.x,
y: body.position.y + body.velocity.y
};
const collisions = Matter.Query.ray(allBodies, startPoint, endPoint);
if (collisions.length > 0) {
// Collision detected along path: handle impact, reposition, or destroy body
}Avoid Direct Position Teleportation
Ensure high speeds are generated via forces, impulses, or velocity
setters rather than modifying body.position directly.
Manually adjusting coordinates skips velocity-based calculations
entirely, almost guaranteeing that the object will phase through
obstacles. If teleportation is necessary, run manual collision checks
prior to placing the body.