Markus Hutnik




2D Physics in the Browser

Intro

These are my notes on creating a website with 2D physics. This is done using the HTML canvas API and Matter.js. Matter.js can be used for rendering graphics as well, but drawing the graphics using a canvas can allow for more flexibility. All the code for this project can be found here.




Setup

I'm using vite for this. To get started initialize a new vite application with the following command:

npm init vite <app-name>

vite init

From this menu, select vanilla. Next, I'm going to use TypeScript.

vite variant

It will ask you to run the following commands:

cd <app-name>
npm install
npm run dev

After running these commands, open the application in your browser to ensure it is working. You should see this screeen.

initial app

Open another terminal and install Matter.js. This is the physics library used for 2D physics. See this page for Matter.js documentation and demos. For TypeScript, it's also necessary to install the types.

npm i matter-js
npm i @types/matter-js

The final step of the setup is to delete the files generate by vite while scaffolding the project. The following files and folders can be deleted. Also, delete all the code from the main.ts and style.css files.

files to delete


Creating the Canvas

Edit the index.html file to match the following. Note the link to style.css and the addition of the canvas.

index

Edit style.css to contain the following.

style

Now add the following code to main.ts. This code gets a reference to the canvas and sets up the main loop for drawing graphics.

canvas setup


Matter.js

First add a new file rectangle.ts. This holds a rectangle class that will be used when creating rectangle bodies with Matter.js. The reason for this is that Matter.js doesn't have an easy way to access the width and height of rectangles. This information is needed when drawing to the canvas, so I just create a simple class that stores the width, height and a reference to the rectangle body. See the ma

rectangle class

Now update main.ts to have the following code. This creates the engine and runner for handling the physics. This also creates our ground and a ball. Notice the ground is given an option isStatic. This tell Matter.js not to apply physics to it (gravity, forces, etc). Objects will still be able to collide with it though. The ball is a circle body placed in the middle of the screen. This body is not static so it will start falling down when it runs. The restitution option tells the "bounciness" of the object. Higher values make it more bouncy.

matter setup


Drawing Objects

After the line with Runner.run add the following code. This adds methods to draw rectangles and circles to the canvas.

drawing objects

When drawing rectangles, use translate and rotate to draw the rectangle with the correct orientation. In the call to ctx.rect(...), I make the x position -rect.width / 2 and the y position -rect.height / 2. This is because in Matter.js, the x and y of bodies are always their centerpoint. In HTML canvas, the x and y define the top left of a rectangle, so this is just compensating for that difference.

For drawing circles, there's no need to rotate since I'm just filling it with a solid color, so it wouldn't look any different if it was rotated. If I were using an image for the circle, I may want to apply the rotation, which could be done using translate and rotate just like drawing rectangles.

Finally, calls to drawRect and drawCircle are added to the animation loop to draw the ground and ball that were created earlier.

One improvement that can be done here is to use events provided by matter as the animation loop instead of the traditional way I have set up here. Lines 38 - 45 above can be replaced with the following. The advantage of doing this is now our animation will start automatically when the runner starts. Also, if we stop the runner, the animation will stop as well. This way it's not two separate things to manage.

animation loop

In the browser, vite will apply the changes as you save files, so no need to refresh. You should now have the ground and a ball that bounces.

ball

Next, I'll add a rectangle so we can see some interaction between bodies. The starting position will be just above the circle, so when they fall down they'll collide. No need to include the isStatic option like the ground becuase it is set to false by default. Add the box to the world as well.

box

Then draw the box to the screen in the animation loop.

draw box

When you go to the browser, you should see this now.

ball and box


Mouse Interaction

The last thing I'm going to show is using the mouse to interact with objects on screen. In Matter.js, this is known as a mouse constraint. This is very simple to do in Matter.js, just add the following lines and add the mouse constraint to the world. The constraint option is not required. I'm using it here to specify stiffness to make the constraint more springy. The stiffness is higher by default, so objects stick to the mouse without any springiness.

mouse constraint


Final Result

final result