Clean Up Matter.js Engine on Component Unmount
Failing to properly dispose of a Matter.js engine when a component unmounts can lead to memory leaks, orphan physics loops, and significant rendering performance issues. This guide details how to completely dismantle and garbage-collect a Matter.js instance by systematically stopping the runner, clearing the world composite, disposing of the renderer, and nullifying references.
1. Stop the Render and Runner Loops
Matter.js operates on an active animation loop typically driven by
Matter.Render and Matter.Runner. If the
component unmounts without halting these, they will continue to execute
in the background via requestAnimationFrame.
// Stop the built-in runner
Matter.Runner.stop(runner);
// Stop the canvas renderer
Matter.Render.stop(render);If you are using a custom requestAnimationFrame
implementation instead of Matter.Runner, ensure you call
cancelAnimationFrame(animationFrameId).
2. Clear the World Composite
To allow bodies, constraints, and composite hierarchies to be garbage-collected, clear the active world instance:
// Remove all bodies, constraints, and composites from the world
Matter.Composite.clear(engine.world, false);Passing false as the second argument ensures that child
composites are cleared without keeping references.
3. Clear the Engine
Clear the engine instance itself to remove any cached state, collision pairs, and broadphase detection grids:
Matter.Engine.clear(engine);4. Remove Canvas and Event Listeners
If the renderer created its own <canvas> element
or bound events to the window, these elements and listeners must be
removed:
// Remove the canvas element from the DOM
if (render.canvas) {
render.canvas.remove();
}
// Clear internal renderer references
render.canvas = null;
render.context = null;
render.textures = {};Additionally, if you bound custom mouse interactions using
Matter.Mouse or Matter.MouseConstraint, remove
the mouse event listeners:
Matter.Events.off(mouseConstraint, 'startdrag');
Matter.Events.off(mouseConstraint, 'enddrag');Implementation Example in a Component Lifecycle
Below is a standard implementation of the complete teardown process
inside a React useEffect cleanup hook, which directly
translates to other component-based frameworks (like Vue's
onBeforeUnmount or Svelte's onDestroy):
useEffect(() => {
const engine = Matter.Engine.create();
const render = Matter.Render.create({
element: sceneRef.current,
engine: engine,
});
const runner = Matter.Runner.create();
Matter.Render.run(render);
Matter.Runner.run(runner, engine);
// Component unmount cleanup function
return () => {
Matter.Runner.stop(runner);
Matter.Render.stop(render);
Matter.Composite.clear(engine.world, false);
Matter.Engine.clear(engine);
if (render.canvas) {
render.canvas.remove();
}
render.canvas = null;
render.context = null;
render.textures = {};
};
}, []);Calling these cleanup methods in order ensures that no hanging animation frames, abandoned physics bodies, or orphaned DOM elements remain in memory when your component leaves the DOM.