JavaScript based 2D Physics Engine

Build your own basic physics engine in JavaScript

  • 7 mins read
by Will Newmarch
Senior Full-stack Developer

Published on Thursday 15th December 2016

A popular and exciting application of JavaScript in web at the moment is physics engines. 

While it's very easy to go and download a well known JavaScript based physics engine library with all the bells and whistles you could supposedly ever want, sometimes the amount of time required for learning the intricacies of these libraries can equal out to the time it takes to tailor your own engine.

Building your own often comes with its own benefits, including knowing the underlying code like the back of your hand and knowing that all the features you require are available to you, not to mention the satisfaction that your system is running purely on your own code, and so, with this in mind I thought I'd lay out a few chunks of code to hopefully inspire others to take a more involved approach on their next physics based project.


Firstly, I'd like to caveat by saying that while the idea of this post is to encourage a deeper understanding of physics engines, anything in the realms of three dimensional physics is probably best done with a library due to the amount of complex maths involved. Two dimensions is plenty when writing from scratch, unless you have a degree in mathematics and a lot of time on your hands.

So without further ado let me provide and explain some simple chunks of code, that will hopefully allow any developer to put together a foundation for their own two dimensional physics engine. 

To begin, we need to set up our basic tools for the job. A basic html5 canvas element, filling the screen...


// Get the width and height of the window
var win = window,
    d = document,
    e = d.documentElement,
    g = d.getElementsByTagName('body')[0],
    width = win.innerWidth || e.clientWidth || g.clientWidth,
    height = win.innerHeight|| e.clientHeight|| g.clientHeight;
    
// Initialise an array to hold the physical objects
var physicalObjects = [];
 
// Initialise the canvas element and set it's width and height
var canvas = document.createElement("canvas");
    canvas.id = "canvas";
    canvas.width = width;
    canvas.height = height;
 
// Append the canvas element to the HTML body
document.body.appendChild(canvas);
 
// Get the canvas's context object
var context = canvas.getContext("2d");


As you can see we're grabbing some measurements of the window size as well as initialising an array to hold 'physical objects', and finally, the canvas element which we set with an 'id' of 'canvas' and set it's width and height to be the same as the window. The getContext() method returns an object that provides methods and properties for drawing on the canvas and this is used later on to effectively 'draw' onto the canvas.

Next we need a basic 'physical' object. This object has 6 basic parameters, allowing it to be rendered on screen. These are an x/y position, a width and a height, and a velocity on the x and y axes.

As you can see the object also has a ‘nextFrame’ function, which lies at the heart of the animation of the object. It updates the object’s position based on it’s velocity every time it is run, and, as is suggested in the method name, is basically a method for updating the object to the next frame position.


var PhysicalObject = function(x, y, w, h) 
{
    // Set the object's x/y position
    this.x = x;
    this.y = y;
    
    // Set the object's width and height
    this.width = w;
    this.height = h;
    
    // Initialise the object's x and y velocity with a value of 0 (it's stationary initially)
    this.xVel = 0.1;
    this.yVel = 0.1;
    
    // Adjust the object's x velocity
    this.addXVel = function(vel) { 
        this.xVel += vel;
    };
    
    // Adjust the object's y velocity
    this.addYVel = function(vel) { 
        this.yVel += vel;
    };
    
    // Update the object's position for the next frame
    this.nextFrame = function() { 
        this.x += this.xVel;
        this.y += this.yVel;
    }
} 


Next we need a 'frame render' function and a loop. This function clears and redraws the whole frame, updating each object via the ‘nextFrame’ function as it goes. 

In the ‘frameRenderLoop’ function you can see that we’re using a function called ‘requestAnimationFrame’. This takes a function as a parameter and runs this function whenever the client is ready to refresh the visual content, and is a function built into most browsers. It usually averages at a rate of 60fps which is more than enough for us, though it is also best practice to use this function when looping an animation in this manner as this is what the function is specifically designed for. 


frameRender = function() 
{
    // Clear view
    context.clearRect(0, 0, width, height);
    
    // For each object in the physicalObjects array...
    for (var i = 0; i < physicalObjects.length; i++) {
        
        // Draw a rectangle on the canvas to represent the object, based on the object's x, y, width and height
        context.fillRect(
            physicalObjects[i].x, 
            physicalObjects[i].y, 
            physicalObjects[i].width, 
            physicalObjects[i].height
        );
            
        // Tell the object to update itself for the next frame
        physicalObjects[i].nextFrame();
    }
} 
     
frameRenderLoop = function() 
{
    // Use requestAnimationFrame to trigger the 'frameRenderLoop' function as soon as the browser is ready to repaint
    requestAnimationFrame(frameRenderLoop);
        
    // Render the current frame
    frameRender();
}


Ok, if we run all this code in the browser and run the following code in the console, we will trigger the frame render loop and add an object into the environment… 


// Start the render loop
frameRenderLoop(); 
     
// Add an object into the engine at x: 100, y: 100, with a width and height of 20 pixels.
physicalObjects.push(new PhysicalObject(100, 100, 20, 20));  
       
// Give it a little push!
physicalObjects[0].addXVel(0.1);


...and BOOM!

Your environment is now running and you can add in and manipulate live objects via the console. This is just a very basic example to demonstrate what can be done, and by allowing the physicalObjects variable to be globally available, we can easily play about with objects and see what’s going on. 

You’ll probably have noticed by now that objects are drifting off screen never to be seen again. We need to adapt our environment slightly to handle this. One way this can be done is with a function picking up on objects that have gone off screen and shifting them instantaneously back on screen on the opposite side, allowing objects to loop across the environment. 

This technique is most famously used in the classic arcade game ‘Asteroids’. The below function requires a physical object to be passed in and ‘actions’ the shift to the opposite edge if it has gone off screen. This function can be included in the render loop to apply this manipulation to objects before being rendered again once they have drifted off screen. 

This function would look something like the following, and would be run on every frame render...


function screenLoop(obj) 
{    
    // Drifted off of right edge 
    if (obj.x - (obj.width / 2) > canvas.width)
        obj.x = -obj.width / 2;
    
    // Drifted off of left edge
    if (obj.x + (obj.width / 2) < 0)
        obj.x = canvas.width + obj.width / 2;
    
    // Drifted off of bottom edge 
    if (obj.y - (obj.height / 2) > canvas.height)
        obj.y = -obj.height / 2;
    
    // Drifted off of top edge
    if (obj.y + (obj.height / 2) < 0)
        obj.y = canvas.height + obj.height / 2;
}


You now have a basic but effective physics engine in your browser, and enough understanding of the mechanics to build upon it and take it to where you want it to be.

The next steps I would take would be to look into some basic collision detection so that objects can interact, and perhaps some object pooling to help with memory management, though hopefully by now you have an understanding of the very basics behind such engines and are encouraged to go and take more control over your physics based projects by building from scratch!