How to Build a Classic Snake Game Using HTML and JavaScript (With Code Example)

Introduction

In this article, I’ll show you how to create a classic Snake game using HTML, CSS, and JavaScript. We’ll use HTML and CSS for the layout and style, and JavaScript to bring the game to life by controlling the snake's movement, detecting collisions, and updating the score.

Snake Game Setup

  1. HTML: Set up the basic structure with a <canvas> for the game area and a score display.
  2. CSS: Add minimal styling to center the game and make it visually appealing.
  3. JavaScript: Define the game logic, including the snake's movement, food generation, collision detection, and score updates.

HTML Code

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game</title>
</head>
<body>
    <h1>Snake Game</h1>
    <canvas id="gameCanvas"></canvas>
    <button class="retry" onclick="restartGame()">Try Again</button>
    <p>Score: <span id="score">0</span></p>
    <script src="script.js"></script>
</body>

CSS Code

The CSS centers the game elements on the page and adds some styling to canvas and the "Try Again" button, which appears when the game ends.

body {
            display: flex;
            flex-direction: column;
            align-items: center;
            font-family: Arial, sans-serif;
            background-color: #333;
            color: #fff;
        }

        h1 {
            margin-top: 20px;
        }

        canvas {
            border: 2px solid #3725db;
            background-color: #000;
        }

        p {
            font-size: 1.5em;
            margin-top: 10px;
        }

        /* Style the Try Again button */
        .retry {
            display: none;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding: 15px 30px;
            font-size: 1.5em;
            color: #fff;
            background-color: #ff6347;
            border: none;
            border-radius: 10px;
            cursor: pointer;
        }

        .retry:hover {
            background-color: #ff4500;
        }

JavaScript Game Logic

The game logic is handled in the script.js file.

Initialize and Setup the Game Canvas and Variables

  • The code first gets the HTML canvas element (gameCanvas) and sets up the 2D drawing context (ctx).
  • Game variables like boxsize, score, snake, direction, food, and game are defined.
  • The initializeGame function sets up the initial game state with a starting snake position, score, and direction, and generates the first food position.
    const canvas = document.getElementById("gameCanvas");
        const ctx = canvas.getContext("2d");
        const retryButton = document.querySelector(".retry");
    
        // Canvas dimensions
        canvas.width = 400;
        canvas.height = 400;
    
        // Game variables
        let boxSize = 20;
        let score = 0;
        let snake;
        let direction;
        let food;
        let game;
    
        // Initialize the game state
        function initializeGame() {
            score = 0;
            snake = [{ x: boxSize * 5, y: boxSize * 5 }];
            direction = "RIGHT";
            document.getElementById("score").innerText = score;
            food = generateFood();
        }
    
        // Generate random food position
        function generateFood() {
            return {
                x: Math.floor(Math.random() * (canvas.width / boxSize)) * boxSize,
                y: Math.floor(Math.random() * (canvas.height / boxSize)) * boxSize
            };
        }

Define Game Logic and Drawing

  • The change direction function updates the snake's direction based on arrow key inputs, ensuring it can't immediately reverse direction.
  • Clears the canvas, draws the food and snake, and moves the snake by updating its head position. If the snake eats the food, the score increases, and new food is generated; otherwise, the tail is removed.
  • The game ends if the snake collides with a wall or itself.
        // Control the snake direction with arrow keys
        document.addEventListener("keydown", changeDirection);
    
        function changeDirection(event) {
            if (event.key === "ArrowUp" && direction !== "DOWN") {
                direction = "UP";
            } else if (event.key === "ArrowDown" && direction !== "UP") {
                direction = "DOWN";
            } else if (event.key === "ArrowLeft" && direction !== "RIGHT") {
                direction = "LEFT";
            } else if (event.key === "ArrowRight" && direction !== "LEFT") {
                direction = "RIGHT";
            }
        }
    
        // Draw the game
        function draw() {
            // Clear the canvas
            ctx.clearRect(0, 0, canvas.width, canvas.height);
    
            // Draw the food as a circle
            ctx.fillStyle = "red";
            ctx.beginPath();
            ctx.arc(food.x + boxSize / 2, food.y + boxSize / 2, boxSize / 2, 0, Math.PI * 2);
            ctx.fill();
    
            // Draw the snake as circles
            ctx.fillStyle = "lime";
            for (let segment of snake) {
                ctx.beginPath();
                ctx.arc(segment.x + boxSize / 2, segment.y + boxSize / 2, boxSize / 2, 0, Math.PI * 2);
                ctx.fill();
            }
    
            // Move the snake
            let head = { ...snake[0] };
            if (direction === "UP") head.y -= boxSize;
            if (direction === "DOWN") head.y += boxSize;
            if (direction === "LEFT") head.x -= boxSize;
            if (direction === "RIGHT") head.x += boxSize;
    
            // Add new head to the snake
            snake.unshift(head);
    
            // Check if the snake has eaten the food
            if (head.x === food.x && head.y === food.y) {
                score++;
                document.getElementById("score").innerText = score;
                food = generateFood();
            } else {
                // Remove the last part of the snake if no food is eaten
                snake.pop();
            }
    
            // Check for collision with walls or self
            if (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height || isCollision(head)) {
                endGame();
            }
        }
    
        // Check if the snake collides with itself
        function isCollision(head) {
            for (let i = 1; i < snake.length; i++) {
                if (snake[i].x === head.x && snake[i].y === head.y) {
                    return true;
                }
            }
            return false;
        }

Game Control Functions

  • The endGame function stops the game loop and displays a "Retry" button, while restartGame hides the button, re-initializes the game, and starts the game loop again.
  • The game is initialized for the first time and starts with a 100 ms interval for each frame.
     // End the game
        function endGame() {
            clearInterval(game);
            retryButton.style.display = "block";  // Show the "Try Again" button
        }
    
        // Restart the game
        function restartGame() {
            retryButton.style.display = "none";  // Hide the "Try Again" button
            initializeGame();
            game = setInterval(draw, 100);  // Restart the game loop
        }
    
        // Start the game for the first time
        initializeGame();
        game = setInterval(draw, 100);

Output

Snake Game

Enhancements

You might include more features.

  • The snake's pace improves as it turns longer.
  • High Score Tracking: Save the high score to local storage.
  • Difficulty Levels: Allow the player to choose the game move from the start.
  • Wall-wrapping: When the snake crosses the screen border, it should show on the opposite side.