Building a Classic Snake Game in JavaScript

Raghav Mrituanjaya

Cover Image for Building a Classic Snake Game in JavaScript

Hey there! Welcome to this exciting tutorial where we'll dive into the world of building a classic Snake game using JavaScript. If you've ever played the Snake game on an old Nokia phone or enjoyed its variations, you're in for a treat. We'll show you how to create your very own version of this addictive game from scratch.

Now, you might be wondering what technologies we'll be using for this adventure. Fear not! We'll primarily be working with JavaScript, the programming language that powers interactivity on the web. Alongside JavaScript, we'll use HTML and CSS to create the game's structure and provide some stylish visuals.

The goal of this tutorial is to guide you through the process of building a fully functional Snake game. We'll start with a blank canvas and take you step by step, explaining every bit of code along the way. By the end, you'll have a cool Snake game that you can play and share with your friends.

So, are you ready to slither into action? Let's get started with the HTML structure and setting up our game area. Get your coding hat on and let's have some fun!

Setting Up the HTML Structure

Alright, let's lay the foundation for our Snake game by setting up the HTML structure. Don't worry, it's nothing complicated. We just need to create a basic structure that will serve as the canvas for our game.

To begin, open up your favourite code editor and create a new HTML file. Inside the file, start with the usual HTML boilerplate by adding the <!DOCTYPE html> declaration and the <html> tags.

Within the <html> tags, let's add the <head> section where we can specify the title of our game. You can go ahead and give it a catchy title like "Snake Game" or get creative with your own unique title.

<!-- Styling -->
<style>
  /* Customize the game area styles */
  #game-area {
    width: 400px;
    height: 400px;
    background-color: #f2f2f2;
    border: 1px solid #ccc;
    margin: 0 auto;
  }
</style>

Moving on to the <body> section, this is where all the action will happen. We'll create an <div> element that will serve as the game area. You can give it an ID like "game-area" to make it easier to reference in our JavaScript code later on.

Now, let's add some styling to make our game area visually appealing. To do this, we'll make use of CSS. You can either include a separate CSS file or add the styles directly within the HTML file using the <style> tags. Feel free to experiment and customize the styles to your liking, such as setting the background colour or adjusting the dimensions of the game area.

<!-- Game Area -->
<div id="game-area"></div>

<!-- Styling -->
<style>
  /* Customize the game area styles */
  #game-area {
    width: 400px;
    height: 400px;
    background-color: #f2f2f2;
    border: 1px solid #ccc;
    margin: 0 auto;
  }
</style>

Alright, with the HTML structure and basic styling in place, we've created a canvas where our Snake game will come to life. In the next section, we'll jump into the exciting part: handling the game logic. Get ready to make things move!

Handling Game Logic

Alright, it's time to dive into the exciting part: handling the game logic. This is where we'll bring our Snake game to life by implementing the necessary JavaScript code to control the game.

First, let's create a new JavaScript file and link it to our HTML file by adding the <script> tag inside the <head> section or just before the closing </body> tag. For simplicity, let's go with the latter approach.

<!DOCTYPE html>
<html>
  <head>
    <title>Snake Game</title>
  </head>
  <body>
    <!-- Game Area -->
    <div id="game-area"></div>

    <!-- Linking the JavaScript file -->
    <script src="snake.js"></script>
  </body>
</html>

In the JavaScript file, let's start by defining some variables to track the game state. We'll need variables for the current score, the direction the snake is moving, the snake's body segments, and the food's position.

// snake.js

// Variables for game state
let score = 0;
let direction = "right";
let snake = [{ x: 10, y: 10 }];
let food = { x: 5, y: 5 };

Next, we'll create a Snake object with properties and methods to control its movement. We can define methods such as move(), changeDirection(), and eatFood().

// snake.js

// Snake object
const Snake = {
  move: function () {
    // Code for moving the snake
  },
  changeDirection: function (newDirection) {
    // Code for changing the snake's direction
  },
  eatFood: function () {
    // Code for eating the food and increasing the score
  },
};

To allow the player to control the snake, we'll need to handle keyboard input. We can create a function called handleKeyPress() that listens for arrow key presses and calls the changeDirection() method accordingly.

// snake.js

// Function to handle keyboard input
function handleKeyPress(event) {
  // Check the pressed key and change snake's direction
  if (event.key === "ArrowUp" && direction !== "down") {
    Snake.changeDirection("up");
  } else if (event.key === "ArrowDown" && direction !== "up") {
    Snake.changeDirection("down");
  } else if (event.key === "ArrowLeft" && direction !== "right") {
    Snake.changeDirection("left");
  } else if (event.key === "ArrowRight" && direction !== "left") {
    Snake.changeDirection("right");
  }
}

// Event listener for keyboard input
document.addEventListener("keydown", handleKeyPress);

Great! We now have the basic structure for handling game logic. In the next sections, we'll continue building the game by adding functions to draw the snake and food, implementing the game loop, and handling game-over conditions.

Stay tuned, as our Snake game is starting to take shape!

Drawing the Snake and Food

In this section, we'll focus on creating functions to draw the snake and food in the game area. We want the snake to be visually represented by a series of connected squares, and the food to be a distinct square.

To achieve this, we'll make use of the HTML canvas element and its 2D drawing context. Let's update our HTML file to include the canvas element within the game area:

<!-- Game Area -->
<div id="game-area">
  <canvas id="canvas"></canvas>
</div>

Next, in our JavaScript file, we'll define a function called drawSnake() to draw the snake on the canvas. We'll loop through each segment of the snake's body and draw a square for each segment using the fillRect() method of canvas context.

// snake.js

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

// Function to draw the snake on the canvas
function drawSnake() {
  // Clear the canvas
  context.clearRect(0, 0, canvas.width, canvas.height);

  // Loop through each segment of the snake's body and draw a square
  snake.forEach((segment) => {
    context.fillStyle = "#000"; // Set the color for the snake
    context.fillRect(segment.x * 20, segment.y * 20, 20, 20); // Draw a square for each segment
  });
}

Now, let's create another function called drawFood() drawing the food on the canvas. We'll set a different colour for the food square to make it distinguishable from the snake.

// snake.js

// Function to draw the food on the canvas
function drawFood() {
  context.fillStyle = "#f00"; // Set the color for the food
  context.fillRect(food.x * 20, food.y * 20, 20, 20); // Draw a square for the food
}

With the drawing functions in place, we can now update the game loop function to call these functions and redraw the canvas in each iteration. Let's call this function gameLoop().

// snake.js

// Function for the game loop
function gameLoop() {
  drawSnake(); // Draw the snake on the canvas
  drawFood(); // Draw the food on the canvas

  // Update the game state and handle game logic here

  requestAnimationFrame(gameLoop); // Call the game loop function recursively
}

// Start the game loop
gameLoop();

Awesome! We now have the ability to draw the snake and food on the canvas. In the next section, we'll focus on implementing the game loop and handling the game state to make the snake move, detect collisions, and update the score.

Keep up the great work, as our Snake game is getting closer to completion!

Implementing the Game Loop and Handling Game State

In this section, we'll implement the game loop and handle the game state to make the snake move, detect collisions, and update the score. Let's continue building on the existing code.

First, let's update the move() method of the Snake object to handle the snake's movement. We'll remove the tail segment and add a new head segment based on the current direction. We'll also handle collision detection within the boundaries of the game area.

// snake.js

const gameAreaWidth = 20; // Number of squares horizontally in the game area
const gameAreaHeight = 20; // Number of squares vertically in the game area

// Snake object
const Snake = {
  // ... previous code ...

  move: function () {
    // Get the current head position
    const head = { x: snake[0].x, y: snake[0].y };

    // Update the head position based on the current direction
    if (direction === "up") head.y--;
    else if (direction === "down") head.y++;
    else if (direction === "left") head.x--;
    else if (direction === "right") head.x++;

    // Check for collision with game boundaries
    if (
      head.x < 0 ||
      head.x >= gameAreaWidth ||
      head.y < 0 ||
      head.y >= gameAreaHeight
    ) {
      // Handle game over condition here
    }

    // Remove the tail segment
    snake.pop();

    // Add the new head segment
    snake.unshift(head);
  },

  // ... remaining code ...
};

Next, let's update the changeDirection() method of the Snake object to handle changing the snake's direction based on the player's input.

// snake.js

// Snake object
const Snake = {
  // ... previous code ...

  changeDirection: function (newDirection) {
    // Update the direction only if it's a valid direction change
    if (
      (newDirection === "up" && direction !== "down") ||
      (newDirection === "down" && direction !== "up") ||
      (newDirection === "left" && direction !== "right") ||
      (newDirection === "right" && direction !== "left")
    ) {
      direction = newDirection;
    }
  },

  // ... remaining code ...
};

Now, let's update the eatFood() method of the Snake object to handle the snake eating the food, increasing the score, and generating new food at a random position.

// snake.js

// Snake object
const Snake = {
  // ... previous code ...

  eatFood: function () {
    // Check if the snake's head position matches the food position
    if (snake[0].x === food.x && snake[0].y === food.y) {
      // Increase the score
      score++;

      // Generate new food at a random position within the game area
      food = {
        x: Math.floor(Math.random() * gameAreaWidth),
        y: Math.floor(Math.random() * gameAreaHeight),
      };

      // Add a new segment to the snake's body
      snake.push({});
    }
  },

  // ... remaining code ...
};

Lastly, let's update the game loop function to call the move() and eatFood() methods of the Snake object. We'll also add a delay using setTimeout() to control the speed of the game.

// snake.js

// Function for the game loop
function gameLoop() {
  drawSnake(); // Draw the snake on the canvas
  drawFood(); // Draw the food on the canvas

  Snake.move(); // Move the snake
  Snake.eatFood(); // Check if the snake eats the food

  // Handle game over condition and update the score

  setTimeout(gameLoop, 200); // Delay the next iteration of the game loop
}

// Start the game loop
gameLoop();

Fantastic! We've implemented the game loop and handled the game state to make the snake move, detect collisions, and update the score. In the final section, we'll wrap up our Snake game by adding the game over the condition and displaying the final score.

Congratulations on coming this far! We're almost there!

Handling Game Over and Displaying the Final Score

In this final section, we'll handle the game over the condition and display the final score when the game ends. Let's complete our Snake game by adding these features.

First, let's update the game loop function to check for the game-over condition when the snake collides with its own body. We'll create a function called checkCollision() to perform this check and handle the game over the condition.

// snake.js

// Function to check for collision with the snake's body
function checkCollision() {
  const head = snake[0];

  // Check if the head collides with any segment of the snake's body
  for (let i = 1; i < snake.length; i++) {
    if (head.x === snake[i].x && head.y === snake[i].y) {
      // Game over condition: snake collided with its own body
      gameOver();
      break;
    }
  }
}

// Function to handle game over
function gameOver() {
  // Display the final score
  alert("Game Over! Your final score: " + score);
  
  // Reload the page to start a new game
  location.reload();
}

// Function for the game loop
function gameLoop() {
  // ... previous code ...

  Snake.move(); // Move the snake
  Snake.eatFood(); // Check if the snake eats the food
  checkCollision(); // Check for collision with the snake's body

  // ... previous code ...
}

Now, when the game-over condition is met, the gameOver() function will be called. It will display an alert message showing the final score and reload the page to start a new game.

Congratulations! You have successfully implemented the game over the condition and the display of the final score. With these additions, the player will be informed when the game ends and can see their achieved score.

Feel free to further enhance your Snake game by adding additional features such as sound effects, high score tracking, or additional levels. Let your creativity guide you as you continue to build upon this foundation.

Enjoy playing and sharing your Snake game with others! Well done on completing this project!

Conclusion

In this tutorial, we've embarked on an exciting journey to build a classic Snake game using JavaScript. Throughout the process, we've covered several essential aspects of game development, including handling game logic, drawing elements on the canvas, implementing the game loop, and handling game-over conditions.

By following the steps outlined in this tutorial, you've gained a solid understanding of how to create a functional Snake game from scratch. You've learned how to control the snake's movement, detect collisions, update the score, and handle player input. Additionally, you've explored the concept of using the HTML canvas element to visually represent the game elements.

Remember that game development is an iterative process, and there's always room for improvement and further customization. You can enhance your Snake game by incorporating additional features such as different levels, power-ups, or even multiplayer functionality.

Now that you have a solid foundation, let your creativity run wild and continue exploring the vast possibilities of game development. Experiment with new ideas, learn from other game projects and keep honing your programming skills.

I hope this tutorial has been both informative and enjoyable for you. Building games is a fantastic way to apply your coding skills, challenge yourself creatively, and have fun along the way.

Happy coding and have a great time playing and sharing your Snake game!

Buy Me A Coffee
Building a Classic Snake Game in JavaScript cover image

Raghav Mrituanjaya

· min read

10+ JavaScript Tips And Tricks for Writing Clean and Efficient Code cover image

Programming

10+ JavaScript Tips And Tricks for Writing Clean and Efficient Code

This post explains to you some tips and tricks in javascript for interminates as well as beginners t...

Raghav Mrituanjaya

· min read

Implementing Authentication In Next.js Using Next Auth cover image

Programming

Implementing Authentication In Next.js Using Next Auth

This tutorial is on implementing custom Google, Facebook, and Twitter OAuth, to your next js app usi...

Raghav Mrituanjaya

· min read


© 2024

THE GOGAMIC BLOG