You should first read part 1 of the tutorial.
This is the second part of our quest to make a simple Snake game in the browser. In this part, we will code the game loop, rendering and controls for the game.
We left off with an empty game board in part 1, with the game state set up. Now it’s time to start implementing the game logic. You can view the result from part 1 here, right click → View Source to see the code.
The game loop
First thing we’ll do is to code the game loop. The game loop is a generic term for the code that constantly runs in the background inside games. In this way, games are different from most other programs in that they require no input from the user to do things. Think about it, most computer programs do not do anything unless you click a key, move the mouse or tap a button. But games do something even when the user is just looking at the screen. The code that runs constantly in the background, is called the game loop.
To run the game loop, we will introduce a new function, that we call
This function will run repeatedly during the duration of the game. It will handle
rendering and moving the snake around the board.
setTimeout call will the the function again, recursively, every second so
that any code inside will run repeatedly in the background.
What we need to do next, is to iterate over the entire board, and update
the DOM (ie. the HTML
div elements we created in part 1) with a different
CSS class if the corresponding cell contains the snake.
We do this by looping over the Y and X axes, and checking if the
snake, if so, we set the element’s class to
"snake". Else we
remove any class set.
We also need to style the
snake class, so it looks like a snake (green).
Add the following inside the
<style> tag to do that:
Now when we refresh the page, the center element on the board should be green. It should look like the example below.
Now that the initial snake is visible, we need to make it to move around the board.
Moving the snake
To make the snake move, we need to extend the game loop.
Before we update the board, we should look at what direction the snake is heading.
Using the direction, we update the position
of the snake (the
snakeY global variables) accordingly. Then we also
need to update the board (the
board[y][x].snake variable). When the board is
updated, it will automatically update
div element with the
snake class, because
the code that does that will follow immediately after our check.
So what does this look like in code? We have the
snakeDirection global variable
to define the direction the snake is heading, right now it always has the value
but we also need to check for
"Down". For each direction,
we change the value of either
snakeY by 1. Changing
snakeY will move
the snake either up or down, changing
snakeX will move the snake left or right.
snakeX = 0 and
snakeY = 0 is the upper left of the board. So
to make the snake move right, we increase
snakeX. To make it go down, we increase
snakeY. And the reverse for left and up. In code, it looks like this:
After updating the snake position, we also update the board at the new position
to set the snake as present. This code should be inserted inside
the double for-loops.
When you run the code, this should be the result:
As you can see, the snake moves up the board slowly, since the direction is always
the next step will be to make it possible to change the direction of the snake.
Next step is handling input. Input will be done using the keyboard (sorry, mobile readers).
To capture this we need to bind the
onKeyDown event. We check what key was pressed and
We create a new function to do this, called enterKey
We also need to add the
onkeydown event handler to the
Here we already have one event handler to initializes the game, so add another
one that handles the key presses:
Inside the function, we will use the another switch statement to check what key was pressed — in this case we check for all the arrow keys — and update the direction of the snake. This will then automatically get picked up by the game loop and the we will be able to control the snake!
The call to
preventDefault is necessary, so that pressing the arrow keys do not perform their
default action (that scrolls the page up/down/left/right).
Try the link below to try it out, note that if you collide with the walls, the game will crash and you can’t play any more. If that happens, refresh to restart the game.
Let’s fix that bug with the walls that makes the game crash, and we’ll have a contrallable snake game!
To check for the walls, we simply need to add an
if statement inside the game loop
after updating the snake position. This will check if the new position is outside the
board by comparing against
0 (snake can’t be on a negative position) and
If we detect that the snake is outside the board, we restart the game by calling
this will reset both the position, length and direction of the snake.
This version of the game is now playable, without crashing. Try it out by clicking below and steering using the arrow keys:
Of course, the snake still does not ‘disappear’ from the board as we move around. So one thing left on the todo list.
Making the snake disappear
To remove the snake, we need to keep track of the tail.
We can use the board to do this, rather than trying to keep track of where different snake segments are on the board. We store how many more iterations of the game loops the Snake should be visible in every cell.
We then simply decrease this as we iterate over the board.
We need to make a few small changes to accomodate this. First, we need
to replace the assignments
board[snakeY][snakeX].snake = 1; to assign
snakeLength instead. So find the two places with that code and
board[snakeY][snakeX].snake = snakeLength;
Now, instead of just storing if there is a snake on the board, we store the remaining length of the snake on that cell.
Next part is changing the loop over the entire board. Instead of checking for
we want to check if the value is greater than zero. If so, there is a snake on that cell,
so we still set the
snake className, but we also decrease the value of
cell.snake by 1.
The update code inside the game loop will look like this:
Try out the result below, I also decreased the
setTimeout call to
1000 / snakeLength so the game is faster:
Now we have an almost playable version of the game. All that remains is collecting those delicious apples (and not being able to move through yourself). We’ll write this code in part 3.