Introduction
Remember a scene from the comic "Smile Campus" where someone shoots forward, and after a while, the bullet hits the back of their head? The author used this dark joke to remind us that the Earth is round.
In the default Matter.js world, there is no boundary. If you throw an object in a direction beyond the canvas boundary, you may never find it again.
To make the Matter.js world "round," you can use the matter-wrap plugin.
What is matter-wrap?
matter-wrap describes itself as:
This plugin allows you to automatically wrap the position of bodies and composites such that they always stay with in the given bounds. Upon crossing a boundary the body will appear on the opposite side of the bounds, while maintaining its velocity. An example of this effect can be seen in the classic Asteroids and Pacman games.
In simple terms, it turns the scene created by Matter.js into a wraping world. When an object crosses a boundary, it appears on the opposite side while preserving its velocity and other physical properties.

Here is a concrete example using matter-wrap:

In this example, I throw a triangle beyond the boundary, and it reappears from the opposite side. That's what matter-wrap does.
Using matter-wrap
To use matter-wrap, you need to install it first. There are two ways: using CDN or npm. Choose the method that suits your project.
CDN
Download the matter-wrap.js or matter-wrap.min.js files from the matter-wrap repository and include them in your project:
<script src="../js/matter.js"></script>
<script src="../js/matter-wrap.js"></script>
<script>
// Your code
</script>
NPM
Install matter-wrap via npm:
npm install matter-wrap
Then import it in your project:
import MatterWrap from 'matter-wrap'
Usage
Let's start with a single shape to make it easy to understand. (For details on creating a canvas and objects with Matter.js, refer to "Interactive Journey in the Physical World: A Matter.js Beginner's Guide.")
Two key steps are required:
- Tell
Matterto use thematter-wrapplugin. - Configure the
pluginproperty when creaitng elements, enablingwrapwith boundary settings.
Here's an example:
<div id="canvas"></div>
<script src="../js/matter.js"></script>
<script src="../js/matter-wrap.js"></script>
<script>
function init() {
// Step 1: Tell matter to use matter-wrap
Matter.use(MatterWrap);
let Engine = Matter.Engine;
let Render = Matter.Render;
let Runner = Matter.Runner;
let Composite = Matter.Composite;
let Composites = Matter.Composites;
let Common = Matter.Common;
let MouseConstraint = Matter.MouseConstraint;
let Mouse = Matter.Mouse;
let Bodies = Matter.Bodies;
// Create engine
let engine = Engine.create();
// Create renderer
let render = Render.create({
element: document.getElementById('canvas'),
engine: engine,
options: {
width: 800,
height: 600
}
});
// Run renderer
Render.run(render);
// Create runner
let runner = Runner.create();
Runner.run(runner, engine);
// Ground
Composite.add(engine.world, [
Bodies.rectangle(400, 600, 1200, 50.5, {
isStatic: true,
render: { fillStyle: '#060a19' }
})
]);
Composite.add(engine.world, [
// Triangle
Bodies.polygon(200, 460, 3, 60, {
// Step 2: Configure plugin with wrap boundary
plugin: {
wrap: {
min: {
x: render.bounds.min.x,
y: render.bounds.min.y
},
max: {
x: render.bounds.max.x,
y: render.bounds.max.y
}
}
}
})
]);
// Mouse event
let mouse = Mouse.create(render.canvas);
let mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: {
visible: false
}
}
});
Composite.add(engine.world, mouseConstraint);
}
init();
</script>
By using render.bounds.min.x, render.bounds.min.y, render.bounds.max.x, and render.bounds.max.y, you can get the coordinates of the canvas corners, defining the object's movement boundaries.
Note: An object will only reappear on the opposite side when it fully leaves the boundary. If only a part goes out, that part will not show on the other side.

If you have multiple elements and want all of them to use matter-wrap boundaries, you can iterate through all bodies and configure them uniformly:
// ... (omitted code)
// Ground
Composite.add(engine.world, [
Bodies.rectangle(400, 600, 1200, 50.5, { isStatic: true, render: { fillStyle: '#060a19' } })
]);
// Create a stack of balls
let stack = Composites.stack(100, 0, 10, 8, 10, 10, function(x, y) {
return Bodies.circle(x, y, Common.random(15, 30), { restitution: 0.6, friction: 0.1 });
});
// Add everything to the world
Composite.add(engine.world, [
stack,
Bodies.polygon(200, 460, 3, 60), // triangle
Bodies.polygon(400, 460, 5, 60), // pentagon
Bodies.rectangle(600, 460, 80, 80) // square
]);
// Get all bodies
let allBodies = Composite.allBodies(engine.world);
// Set wrap plugin for each body
for (let i = 0; i < allBodies.length; i++) {
allBodies[i].plugin.wrap = {
min: { x: render.bounds.min.x, y: render.bounds.min.y },
max: { x: render.bounds.max.x, y: render.bounds.max.y }
};
}
The result is:

That covers the whole article.
Recommended Reading
👍 Interactive Journey in the Physical World: A Matter.js Beginner's Guide