Xander Gottlieb

Xander Gottlieb smiling at the camera
I'm Xander, a software engineer in Sussex, UK.

Matter.js — The Missing Tutorial

Matter.js is an absolutely fantastic 2D physics engine built on HTML5 Canvas. Unfortunately, while it has comprehensive documentation and plenty of cool demos, there is little introduction for absolute beginners. This tutorial aims to fix that.

In this tutorial, we’re going to run through the fundamentals of Matter JS with the goal of building an interactive bouncy ball.

Get the gist at the bottom of this article.

Getting Started

The first step is to download the latest release of matter.min.js from GitHub and include it on your page.

The only markup we need to get things going is a canvas with an arbitrary id so that we can fetch it easily from JavaScript.

<canvas id="world"></canvas>

In a fresh JavaScript file we can begin setting up our virtual world:

var myCanvas = document.getElementById("world");
var engine = Matter.Engine.create();
var world = engine.world;
var render = Matter.Render.create({
  canvas: myCanvas,
  engine: engine,
  options: {
    width: width,
    height: height,
    background: "transparent",
    wireframes: false,
    showAngleIndicator: false,
  },
});

Note: all code relying on Matter.js must take place after the page load.

Here we are fetching our canvas element and then creating a new engine, world and renderer from Matter.js. The engine handles all the clever physics such as gravity and collisions; the world stores a collection of every body in the world; and the renderer takes charge of actually displaying the visuals on the canvas.

Our engine and world will work straight away but as you can see the renderer requires a little extra configuration. Choose your own width and height. Our background can be any valid CSS colour. The wireframes and showAngleIndicator parameters are useful for debugging visual issues by displaying bodies as white outlines with indicators showing their upright positions.

Adding Bodies

var ball = Matter.Bodies.circle(x, y, radius, {
  density: 0.04,
  friction: 0.01,
  frictionAir: 0.00001,
  restitution: 0.8,
  render: {
    fillStyle: "#F35e66",
    strokeStyle: "black",
    lineWidth: 1,
  },
});
Matter.World.add(world, ball);

Each Body is added using a similar constructor function. For our ball, we create a circle with an x and y coordinate to be placed at, along with a radius to define its size. Bodies have a bunch of physical properties which are great fun to play with (see the docs for more, in particular render.sprite which can be used to texturise your bodies with images).

Interactivity

var mouseConstraint = Matter.MouseConstraint.create(engine, {
  //Create Constraint
  element: myCanvas,
  constraint: {
    render: {
      visible: false,
    },
    stiffness: 0.8,
  },
});
Matter.World.add(world, mouseConstraint);

The method of making bodies draggable is to define a MouseConstraint. This works like a virtual spring that attaches to the mouse. When dragging begins on a body, the spring is attached and pulls in the direction of mouse movement, releasing the body on mouseup. The best way to get to grips with this idea is to set visibility to true and have a play with the stiffness value for yourself.

Scrolling Broken? Easily fixed.

By default, MouseConstraint captures scroll events, to prevent this behaviour use the following snippet:

mouseConstraint.mouse.element.removeEventListener(
  "mousewheel",
  mouseConstraint.mouse.mousewheel,
);
mouseConstraint.mouse.element.removeEventListener(
  "DOMMouseScroll",
  mouseConstraint.mouse.mousewheel,
);

Running

Matter.Engine.run(engine);
Matter.Render.run(render);

With the two lines above we can finally start seeing what we’ve created.

Unfortunately if you run the page now, you’ll see that our ball forms and then plummets of the screen with reckless abandon. Thankfully, this is easily and intuitively fixed.

We’re Gonna Build a Wall

In order to stop the ball falling out the bottom of the canvas, we simply have to add a Body to act as a floor.

var floor = Matter.Bodies.rectangle(x, y, width, height, {
  isStatic: true,
  render: {
    visible: false,
  },
});
Matter.World.add(world, floor);

Note: The co-ordinate system in Matter.js places a body’s origin at its centre.

We create a rectangle, place it at the centre-bottom, give it a width equal to the width of the canvas and a reasonable height (~40px tends to work) in order for the engine to correctly detect collisions. isStatic prevents the floor from being moved by collisions with other bodies.

You should now be able to write your own left and right walls as well as a ceiling.

Showtime

All there is to do now is kickstart the motor of your shiny new physics engine and enjoy the cool breeze whipping at your hair as it motors you towards exciting new interactive web projects!

A Note

Bodies that fall outside the canvas remain in memory. Therefore, if your project is going to send stuff flying off the screen, make sure you garbage collect!

Matter.World.remove(world, body);

Pro Tip

Matter.js uses pixel co-ordinates which can be frustrating for building responsive, full-screen apps. My solution is to set the width and height of the render object to be window.innerWidth and window.innerHeight and then use functions like those below to size bodies relative to the viewport.

function percentX(percent) {
  return Math.round((percent / 100) * window.innerWidth);
}
function percentY(percent) {
  return Math.round((percent / 100) * window.innerHeight);
}

Get the Gist