Deactivate Off-Screen Bodies in Matter.js
This article explains how to optimize performance in a Matter.js
physics simulation by deactivating off-screen bodies. You will learn how
to detect when a body leaves the active viewport and how to disable its
physics calculations using either the Matter.Sleeping
module or the isStatic property, reducing unnecessary CPU
load during intensive simulations.
Why Deactivate Off-Screen Bodies
When running simulations with large worlds or many entities, computing collisions and motion for objects outside the visible camera view wastes processing power. Deactivating these bodies pauses their computational overhead until they re-enter the screen.
In Matter.js, you have two primary methods to deactivate a body:
- Sleeping flags (
Matter.Sleeping): Officially suspends broadphase and narrowphase collision checks while preserving internal momentum states. - Toggling
isStatic: Converts the dynamic body into an immovable static body, stopping all gravitational and force integration.
Method 1: Using the Sleeping Module
Matter.js has built-in sleeping support. Sleeping bodies are skipped during broadphase collision checks, making this the cleanest and most idiomatic method.
1. Enable Sleeping on the Engine
Before bodies can sleep, enable the feature on your engine instance:
const engine = Matter.Engine.create({
enableSleeping: true
});2. Put Off-Screen Bodies to Sleep
Use Matter.Sleeping.set(body, isSleeping) to manually
force a body to sleep or wake it up:
// Put body to sleep
Matter.Sleeping.set(body, true);
// Wake body up
Matter.Sleeping.set(body, false);Method 2: Toggling
isStatic
If you do not want to enable global sleeping or need the body to act
as a solid, immovable obstacle while off-screen, you can toggle
isStatic.
Caveat with Velocity
Setting isStatic: true sets the body's velocity and
angular velocity to zero. If you want the body to resume its previous
trajectory when returning to the screen, store its velocities before
making it static:
// Deactivating
body.savedVelocity = { ...body.velocity };
body.savedAngularVelocity = body.angularVelocity;
Matter.Body.setStatic(body, true);
// Reactivating
Matter.Body.setStatic(body, false);
Matter.Body.setVelocity(body, body.savedVelocity);
Matter.Body.setAngularVelocity(body, body.savedAngularVelocity);Implementing the Viewport Check Loop
To automate deactivation, monitor your bodies' positions relative to
the screen boundaries during each update cycle using the
beforeUpdate event.
const viewport = {
minX: 0,
maxX: 800,
minY: 0,
maxY: 600
};
Matter.Events.on(engine, 'beforeUpdate', () => {
const bodies = Matter.Composite.allBodies(engine.world);
for (let i = 0; i < bodies.length; i++) {
const body = bodies[i];
// Skip permanent static geometry like world boundaries
if (body.isPermanentStatic) continue;
const isOffScreen =
body.bounds.max.x < viewport.minX ||
body.bounds.min.x > viewport.maxX ||
body.bounds.max.y < viewport.minY ||
body.bounds.min.y > viewport.maxY;
if (isOffScreen && !body.isSleeping) {
Matter.Sleeping.set(body, true);
} else if (!isOffScreen && body.isSleeping) {
Matter.Sleeping.set(body, false);
}
}
});Using body.bounds ensures that the body is completely
outside the visible area before deactivation occurs, preventing
premature freezing while part of the body is still on-screen.