Skip to content
Sign upLog in
← Back to Community

Basic Platformer With Javascript and HTML

Profile icon


End Result + Code are below this post!

Prerequisites And Advice:

For this tutorial you should already have a basic understanding of programming (preferrably in Javascript) as I will not be explaining basic Javascript concepts in this tutorial. If you wish to learn some/more Javascript I recommend you visit:


It is best if you follow along using a HTML + CSS + JS REPL, as when created the REPL will already have all the boiler plate stuff, but also because you will be able to see the result straight away on the same screen (when you run the code).

The first (and most important) line of code you will write will be in the index.html file. Don't worry this is the only bit of HTML you will have to write :).
Place the line of code after the opening body tag but before the script tag, like so:

<body> <canvas id="canvas" width="512" height="512"></canvas> <script src="script.js"></script> </body>

Make sure the id of the canvas is "canvas", but you can change the width and height to whatever number you want as long as the width and height are multiples of 32!

Navigate yourself to the script.js file where we will be spending the remainder of this tutorial. The first thing that we need to do is to create a place in which we can store the attributes of the player, this will be done using an Object. For starters our player needs to have an X coordinate, a Y coordinate, a Width and a Height. For simplicity's sake the player will be a 32 x 32 pixel square, and the initial X will be set to half the canvas width, and Y will be set to half the canvas height.

const player = { x: 256, y: 256, width: 32, height: 32 }

Make sure the final value doesn't have a comma after it!
At the top of the Javascript file we need to create a reference to the canvas element we created in the first step like so:

const c = document.getElementById("canvas").getContext("2d");

It basically gets the canvas element -> specifys that we will be working in two dimensions -> sets the value of c to that.

Now we need a way to draw the player at its location with its size. To do this we will create a draw function, inside it we will need to specify the fill colour of the player and how to draw the player.
To specify the colour to fill the next object drawn, you update the canvas' fill colour.

c.fillStyle = "red";

You can set the colour to whatever you want.
On the next line you need to actually draw the player rectangle using a canvas function called fillRect which draws and fills the rectangle at the same time. The fillRect function takes four parameters in the following order: an x value, a y value, a width and a height.

c.fillRect(player.x, player.y, player.width, player.height);

As shown above, you access the attributes of an object using the template: objectName.attribute.
To call this draw function: at the bottom of the Javascript file put:


If you followed the steps above correctly your Javascript code should look like this:

const c = document.getElementById("canvas").getContext("2d"); const player = { x: 256, y: 256, width: 32, height: 32 } function draw(){ c.fillStyle = "red"; c.fillRect(player.x, player.y, player.width, player.height); } draw();

When you run this code you should see a red square appear somewhere in the top-left of your screen.
Now remove the line calling the draw function as it was only for testing.

Going Loopy

Every meaningful game needs to have a main function that is looped, to update and draw new things so fast that it is almost imperceptible to the human eye. For our game we need a main function that is called when all the updating for one frame is done. So create a function called main, and inside you can put a call to the draw function:

function main(){ draw(); }

In this state the main function will only be called once, to fix this we need to put another line below the call to draw that re-runs the main function.

function main(){ draw(); requestAnimationFrame(main); }

It basically allows the HTML to render what is drawn and then calls the main function again.


If you run the code you will notice that nothing happens, this is because nothing calls the main function in the first place. What we need is something that runs when the page is fully loaded, luckily Javascript has a function just for this:

window.onload = function(){ main(); }

When the page loads, whatever is in the function will be executed, so if you run the code now you should now see the red square appear again!

Level Making And Drawing

We will be making this game's level using a tile map based system because it is easy to use and design. The main concept is that there will be a multi-line string with each line being a certain length long filled with either a 0 or a 1 (Air or a Wall). You can define the level variable like:

const level = `0000000000000000 0000000000000000 0010000000000000 0000000000001111 0000111000000000 0000000000011111 0000000000000000 0000000000111111 0000000000011000 1110000000000000 0000000010000110 0001111111100000 0000000000000000 0000000000000000 0000000001111110 0000000000000000`;

This gives us the ability to create multiple levels really easily. Feel free to tweak the positions of the 1s and 0s until you see fit.

Side Note: Levels should really be defined in external text files, but file handling would distract us too much from the making of the game so I decided just to define it in the Javascript.

Before being able to use this level data we need to make a function that parses it into a 2D Array (Arrays within arrays (but only to one layer)). We need to:

  • Split the string up by line
  • Split each line up by character
  • Return that result
    To split by line and store the result in an array we can do:
const lines = lvl.split("\n");

Where lvl is the level data that we pass to the function.
To split each line in the array by characters we need to use the map function (Introduced in ES5).

const characters = => l.split(""));

If you are unsure on how to use the map function refer to this.
The final level-parsing function should look like:

function parse(lvl){ const lines = lvl.split("\n"); const characters = => l.split("")); return characters; }

To make the level data accessible to all of the program we need to define a variable in the global scope, at the top of the Javascript file, below the c variable, write:

let currentLevel;

This just tells Javascript that we want this variable to be declared but that we don't need to use it yet.
In the window.onload function update the value of currentLevel to the return value of the parse function:

window.onload = function(){ currentLevel = parse(level); main(); }

Now all we need to do is draw the level!
In the draw function we need to loop through each of the level data lines and on each line we need to loop through each character, and if the character is a 1 then we need to draw a square at the position on canvas relative to the character's position. Also before the looping we need to define a new colour for walls!

function draw(){ c.fillStyle = "red"; c.fillRect(player.x, player.y, player.width, player.height); c.fillStyle = "black"; for (let row = 0; row < currentLevel.length; row++) { for (let col = 0; col < currentLevel[0].length; col++) { if (currentLevel[row][col] === "1") { c.fillRect(col * 32, row * 32, 32, 32); } } } }

Here we are using that fillRect function again, but instead of the player we are drawing each wall tile.
If you now run the code you should see squares that match what is written in the level variable.

Handling User Input

Our game right now is pretty boring as there is nothing to do, only stare at the beautifully arranged squares that should now populate you screen. To fix this we need to add a way to listen for keyboard input and move the player based on which key is pressed. At the top of the Javascript file write the line:

let keysDown = {};

This is an empty object which will hold the keys currently pressed. If you are wondering why we need to store current keys down it is because this way allows multiple keys to be pressed at once.
To listen for key presses, and then store that key press in thekeysDown variable we will use an eventListener:

addEventListener("keydown", function(event){ keysDown[event.keyCode] = true; });

This function executes whenever the user presses a key on their keyboard, the key pressed information is stored in the event parameter, it then sets the key to true in keysDown. Now we need a similar function that executes when a key is released:

addEventListener("keyup", function(event){ delete keysDown[event.keyCode]; });

It removes the entry of the released key from keysDown.

To detect and act upon keys that are pressed we need to create an input function that checks whether certain keys are pressed.

function input(){ if(65 in keysDown){ player.x -= 3; } if(68 in keysDown){ player.x += 3; } }

In Javascript keys are represented numerically, in this example 65 is the number for the "A" key and 68 is the number for the "D" key, if you wish to find out the keycodes for other keys you can use this website.
In main you need to call this function so that input is checked every time main is executed. If you run the code you should see that when you press "A" or "D" the player moves horizontally, but you may also notice that the red square's "previous states" are still visible, to fix this write the following line at the top of the draw function:

c.clearRect(0, 0, canvas.width, canvas.height);

Horizontal Collisions

When moving the red square you will notice that it passes straight through walls, this is because we haven't defined any collisions yet! But before we do collisions we need to create a function called getTile that gets the tile at a certain X and Y value:

function getTile(x,y){ return(currentLevel[Math.floor(y / 32)][Math.floor(x / 32)]); }

To start collisions we need to define another player attribute called speed, this will determine how fast the player can move horizontally:

const player = { x: 256, y: 256, width: 32, height: 32, speed: 3 }

Now move back to the input function and add two if statements around the updating of the player.x value, and update the previous code to add player.speed instead of 3:

function input(){ if(65 in keysDown){ if (getTile((player.x - player.speed) + 1, player.y + 16) !== "1") { player.x -= 3; } } if(68 in keysDown){ if (getTile(((player.x + player.width) + player.speed) - 1, player.y + 16) !== "1") { player.x += 3; } } }

This checks whether the tile, when the player will move by player.speed, at the player's location is a wall or air, if it is air then the player is allowed to move in the direction, else do nothing.
If you now run the code, the red square should stop whenever it hits a wall.


This section will involve a little bit of physics, if you are unaware of how gravity and jumping works you can look at this website, but note that this knowledge isn't required to follow along the next section (it just clarifies things).

Firstly we need to update the player object with three attributes required for calculating Gravitational Potential Energy, they are:

  • Mass (mass)
  • Kinetic Energy On The Y-Axis (yke)
  • Gravitational Potential Energy (gpe)
const player = { x: 256, y: 256, width: 32, height: 32, speed: 3, mass: 64, yke: 0, gpe: 0 }

Now we need to create a function to update the player's Y position, the player's Y Kinetic Energy and the player's GPE, but first we will create a function that takes the player as a parameter and calculates the GPE of it. GPE = mass * 9.8 * height. As we will be working in pixels instead of meters we will need to divide the Gravitational Field Strength (9.8) by a million so it scales correctly, and as (0,0) on canvas is the top left we need to take the player's Y value from the height of the canvas (512 in my case), and finally so the GPE doesn't increase so quickly per pixel we will divide the player's 'height' by 32.

function calcGPE(obj) { return obj.mass * (9.8 / 1000000) * ((canvas.height - obj.height) - (obj.y / 32)); }

Now that that is out of the way we can create a function called gravity that takes the player as a parameter and:

  1. Takes yke away from y
  2. Takes GPE away from yke
  3. Recalculates GPE
function gravity(obj){ obj.y -= obj.yke; obj.yke -= obj.gpe; obj.gpe = calcGPE(obj); }

Now add a call to gravity in the main function and pass player to it. Now if you run the code you should see that the red square now falls off the screen in a realistic manner.

Vertical Collisions

When our red square falls, it goes straight through the walls/floors, to fix this we need to add some more code to the gravity function. Firstly we will check for downwards collisions (landing on the floor), to do this we need to utilise the check squares function to see if the tile at the player's feet is a wall, if yes then we need to set the player's Y Kinetic Energy to 0 and move the player up until it looks seamless with the floor. Also as a preventative measure against future bugs we will need to check that the player is actually falling (Y Kinetic Energy is less than 0).

// Place Below Previous Code Written if (getTile(obj.x + 32, (obj.y + 32)) !== "0" || getTile(obj.x, (obj.y + 32)) !== "0") { if (obj.yke <= 0){ obj.yke = 0; obj.y -= (obj.y % 32); } }

Now we need to do the same for upwards collisions, and also we should only check one vertical collision if the other is false.

if (getTile(obj.x, obj.y) !== "0" || getTile(obj.x + 32, obj.y) !== "0") { if (obj.yke >= 0){ obj.yke = -0.5; obj.y += 1; } }

This does almost the same thing except it sets the Y Kinetic Energy to a small negative number so that it doesn't get stuck on the wall above.
Your gravity function should now look like this:

function gravity(obj) { obj.y -= obj.yke; obj.yke -= obj.gpe; obj.gpe = calcGPE(obj); if (getTile(obj.x, obj.y) !== "0" || getTile(obj.x + 32, obj.y) !== "0") { if (obj.yke >= 0){ obj.yke = -0.5; obj.y += 1; } } else { if (getTile(obj.x + 32, (obj.y + 32)) !== "0" || getTile(obj.x, (obj.y + 32)) !== "0") { if (obj.yke <= 0){ obj.yke = 0; obj.y -= (obj.y % 32); } } } }

On running the code you can see that the red square stops when it hits a wall!


Congratulations if you made it this far (this tutorial is very long...), this will be the final section before the basic platformer is complete! The final thing that we need to add is jumping. If we just move the player's Y value by a certain amount the jumping will look very unnatural, so instead we will increase the player's Y Kinetic Energy which gives it a nice realistic looking jump. The keycode for the "W" key (the jump key) is 87, so in the input function add a check for if 87 is in keysDown and if so increase the player's Y Kinetic Energy by however much you desire (5-8 is generally good), but to disallow the player to jump when directly below a wall we need to add a check to see if they are (and disallow jumping).

if (87 in keysDown && player.yke === 0) { if (getTile(player.x,player.y - 1) !== "1" && getTile(player.x + 32,player.y - 1) !== "1"){ player.yke += 8; } }

This also checks whether the player's Y Kinetic Energy is 0 (is on floor) to prevent mid-air jumping.
And now... if you run the code you should be able to jump!


Thank you for taking the time to read through this tutorial!

by llambda

Profile icon
Profile icon
Profile icon
Profile icon
Profile icon
Profile icon
Profile icon
Profile icon
Profile icon
Profile icon
Profile icon

realll nice

Profile icon

Nice enough for an upvote? :)

Profile icon

This is awesome!

Profile icon

where can i find the files?

Profile icon

Good job my young codrs stuck at home
you know what they say
Coronavirus:making coders since 2020

Profile icon

Good tutorial! All it needs is edge collision detection because if you go off the edge of the screen you fall through the platforms.

Profile icon

I agree, if you read How To Make A Puzzle Platformer this problem is addressed and fixed. All you have to do is change the !== "1" in the horizontal collisions to === "0" so that it only allows movement when the space in front is a non solid block ("0"). This also allows you to create new blocks that automatically have horizontal collisions.

Profile icon

I found a glitch, you go to a specific spot and you phase through it.

Profile icon

I just did this walkthrough and I really enjoyed it. I learned a lot and made my first basic game. Thank you!

Profile icon

i typed it out word for word and copy pasted when that didnt work and still it just shows me this instead of a square

throw err;

Error: ENOENT: no such file or directory, open '/home/runner/2d-platformer/index.js'
at Object.openSync (fs.js:462:3)
at Object.readFileSync (fs.js:364:35)
at Object. (/run_dir/interp.js:195:19)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47 {
errno: -2,
syscall: 'open',
code: 'ENOENT',
path: '/home/runner/2d-platformer/index.js'

Profile icon

I finished it! I have experience in python, but non in javascript. I applied my knowledge of functions and more into this, and hey! it works! Ima start adding velocity to the x-axis. (yes, I use scratch, that's why)

Profile icon

How exactly would you make levels? i don't understand.

Profile icon

. . . waaard . . .

Profile icon

Truly Great, And thanks for the tutorial

Profile icon

Looks like fun.

Profile icon

When I tried it, the player can't move right at all. What's the fix for that?

Profile icon

I had just misspelled a variable name in the code, it's fixed now.

Profile icon

is there a url to go to?

Profile icon

how to do file handling i need to know for a platformer of my own

Profile icon

Did you delete the platformer? I can't play or use the game anymore.

Profile icon

Is there a way to make the tiles randomized? If so, how?

Profile icon

I believe the code has a function in it to randomize the tiles. If you look in the javascript file, it should be there.