Building a Snake Game using Canvas

CT Solutions
Level Up Coding
Published in
7 min readApr 20, 2020

--

Building a Snake Game using Canvas

The HTML “canvas” element is one of the new elements added in HTML5. It is a container of fixed size that allows you to draw graphics in it using JavaScript. Canvas has several methods for drawing paths, boxes, circles, text, and adding images. Interactive animations and games can also be created.

The <canvas> element is a drawable region defined in HTML code with height and width attributes as follows:

<canvas id=”firstCanvas” width=”500" height=”500"></canvas>

The ‘id’ here is important as it is used to target this element in JavaScript using the Document Object Model (DOM) as follows:

var canvas = document.getElementById(“firstCanvas”);

In JavaScript, the drawing methods are not called on the canvas element, but on the drawing context which is acquired by calling .getContext(‘2d’) as follows:

var context = canvas.getContext(“2d”);

This method identifies the context of the targeted canvas element, which means one will have access to the canvas drawing API that provides methods and properties for drawing on the canvas. For example, if we want to draw a rectangle in the canvas, we can use the context.rect() method as follows:

context.rect(20, 20, 100, 100);

This method takes four arguments: x and y coordinates of the upper-left corner of the rectangle and the width and height of the rectangle: rect(x, y, width, height). The method context.stroke() needs to be called to show the shape on the canvas. It will look like this:

rectangle created by using canvas

Similarly, a filled rectangle can be drawn using the .fillRect() method that accepts the same arguments. The fill colour can be defined using the context.fillStyle method before calling the .fillRect method:

context.fillStyle= "#6c2c3a";context.fillRect(20, 20, 100, 100);

The following image shows the rectangle created by the above lines of code:

To draw a circle, the method context.arc() can be used that takes the coordinates of the centre, the radius of the circle, and starting and ending angle in radians, which in case of the circle would be 0 to 2*Math.PI (360° in radians). Other shapes like triangles can also be drawn by drawing a line for each side using the lineTo() method. Other than shapes, we can also draw text, insert images and add transformations such as scale(), rotate(), translate() etc.

It’s also very easy to make interactive animations (the ones that react to mouse clicks, keyboard key presses, button clicks, finger movements on touch screens etc). For example, the following snake game is based on button click and arrow key presses (for changing the snake’s direction).

Steps of building a basic snake game:

Making the canvas

The first step is to make a <canvas> element and set its dimensions. The background of the drawing surface, i.e. the <canvas> element can be set by using CSS or JavaScript methods.

Setting the movement

In this step, the canvas is set up to make movements in the game. For this, we clear the canvas area before showing the next position of the elements (snake, fruit etc), update their coordinates and then show them again, and this process is repeated till the game is over. It is achieved by using the JavaScript method window.setInterval(function, milliseconds). This method repeats a given function at every specified time interval. Time is specified in milliseconds.

In this game, the function defined in the window.setInterval basically does three things. First, it draws the fruit at some position. Secondly, it updates the snake position and displays it. Then it checks if the snake has eaten the fruit, and updates the fruit position and scores according to it.

function main() {//update state of game and display changes after specified intervalinterval = window.setInterval(() => {context.clearRect(0, 0, 500, 500);drawFruit();moveSnakeForward();drawSnake();//check if snake eats the fruit - increase size of its tailand find new fruit positionif (snakeHeadX === fruitX && snakeHeadY === fruitY) {totalTail++;fruitPosition();}}, 200);}

Creating methods for drawing the snake

This is achieved by creating two functions, drawSnakeHead() and drawSnakeBody(). The snake is drawn using x and y coordinates of each part of its body, where x and y represent the top left corner of that part.

In the method drawSnakeHead(), the variable snakeHeadX and snakeHeadY store the x and y value of the snake’s head respectively. The method context.arc is used here to draw a circle for the head part. Snake’s eyes can be drawn similarly by choosing appropriate coordinates. The variable ‘scale’ stores the number of pixels each part of the snake’s body takes.

function drawSnakeHead(color) {context.beginPath();context.arc(snakeHeadX+scale/2, snakeHeadY+scale/2, scale/2,0, 2 * Math.PI);context.fillStyle = color;context.fill();}

The array tail[] stores coordinates of each part of the tail. The function drawSnakeBody() contains a loop that draws each part of the tail iteratively using the ‘tail’ array. Here, the variable ‘tailRadius’ is simply used to increase the size of the snake tail from back to front. So, if the tail length is 4, the snake looks like this:

snake body created using canvas
function drawSnakeTail() {let tailRadius = scale/4;for (i = 0; i < tail.length; i++) {tailRadius=tailRadius+((scale/2-scale/4)/tail.length);context.beginPath();context.fillStyle = "#6c2c3a";context.arc((tail[i].tailX+scale/2), (tail[i].tailY+scale/2), tailRadius, 0, 2 * Math.PI);context.fill();}}

The above two functions are then called in drawSnake() method which executes in the window.setInterval() function.

The function moveSnakeForward() shifts the tail and snake head’s coordinates to the next position. The variables xSpeed and ySpeed represent the movement in a particular direction, i.e. if the snake moves horizontally, the xSpeed will be equal to ‘scale’(positive for right and negative for left direction) and ySpeed will be zero and vice-versa. So, the new value of the snake’s head is assigned using these variables.

function moveSnakeForward() {tail0=tail[0];for (let i = 0; i < tail.length - 1; i++) {tail[i] = tail[i + 1];}tail[totalTail - 1] = { tailX: snakeHeadX, tailY: snakeHeadY };snakeHeadX += xSpeed;snakeHeadY += ySpeed;}

Moving the snake based on the direction

A method is created to change the direction of the head of the snake based on the key pressed by the player. The function changeDirection() uses a switch case and takes the details of the arrow key pressed using the “keydown” event to change the direction. For example, if the Up arrow key is pressed:

switch (directionVar) {case "Up"://move "up" only when previous direction is not "down"if (previousDir !== "Down") {direction=directionVar;xSpeed = 0;ySpeed = scale * -1;}Break;//similar cases for other directions}

The variable directionVar contains details of the key pressed by the player. The previous direction is stored in a variable previousDir. As the snake moves up (vertical direction), xSpeed is turned to 0 (no horizontal movement), and ySpeed to negative. Similar cases can be defined for other directions by choosing the appropriate value for xSpeed and ySpeed.

Creating Fruit

A method generateCoordinates() generates random x and y values within the limits of the canvas height and width which can be used for the position of fruit. A square can be made at this position using fillRect() method. Alternatively, an image of fruit can be inserted at this position using drawImage() internal method.

Checking Collision

At every interval, it is required to check if the snake collided with the boundaries of the surface or with its tail itself (as shown in the code below).

//check snake's collisionfunction checkCollision() {let tailCollision=false, boundaryCollision=false;//with its own tailfor (let i = 0; i < tail.length; i++) {if (snakeHeadX == tail[i].tailX && snakeHeadY == tail[i].tailY) {tailCollision=true;}}//with boundariesif(snakeHeadX >= canvas.width || snakeHeadX < 0 || snakeHeadY >= canvas.height || snakeHeadY < 0){boundaryCollision=true;}return (tailCollision || boundaryCollision);}

The above method is called in the function drawSnake() as follows:

//display snakefunction drawSnake() {drawSnakeHead("#7d4350");drawSnakeTail();if (checkCollision()) {clearInterval(gameInterval);setTimeout(()=>{scoreModal.textContent = totalTail;$('#alertModal').modal('show');modalBtn.addEventListener("click", ()=>{context.clearRect(0, 0, 500, 500);score.innerText = 0;   });  }, 1000); }}

If a collision happens, a message is displayed to the player containing the text “Game Over” along with the final score. Here, a bootstrap modal is used for this purpose. The text of the <span> element (in the modal body) with id ‘scoreModal’ is set to the final score as follows:

scoreModal.textContent = totalTail;

Then this modal is displayed on the screen using jQuery. The method clearInterval() stops the defined interval that updates the canvas (position of a snake, fruit etc).

This gives a review of methods for creating a basic snake game.

To increase the difficulty of the game, hurdles for the snake can be created in a similar way. Functionality such as pausing and resuming the game can also be added on pressing the spacebar by using clearInterval() for pausing the game and calling the main() method again to resume the game on alternate spacebar keypresses.

For full code, go to GitHub.

Here, a virus is drawn at random positions after a specific interval. The game finishes when the snake eats it. Create your own game and enjoy. 🤗

--

--

Driven by the zeal to bring in a change in the digital world, Catalyst Technology Solutions will evolve your brand globally.