DEV Community

Alexya
Alexya

Posted on

The old school snake game with just 100 lines of JS code

If you grew up in the late '90s or early 2000s, you probably remember the classic Snake game.

It was simple, addictive, and a PUBG of early mobile gaming. I recently decided to recreate it using just 100 lines of Javascript.

Here is the HTML:

<!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>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="container">
    <h1>Snake Game</h1>
    <div id="score">Score: 0</div>
    <div id="game-board"></div>
    <button id="start-restart">Start / Restart</button>
  </div>
  <script src="script.js"></script>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

CSS:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: Arial, sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #f3f3f3;
}

.container {
  text-align: center;
}

#game-board {
  display: grid;
  grid-template-columns: repeat(20, 20px);
  grid-template-rows: repeat(20, 20px);
  gap: 1px;
  background-color: #e0e0e0;
  margin: 20px auto;
  width: 400px;
  height: 400px;
  position: relative;
}

.cell {
  width: 20px;
  height: 20px;
  background-color: #f3f3f3;
}

.snake {
  background-color: green;
}

.food {
  background-color: red;
}

#score {
  font-size: 20px;
  margin: 10px 0;
}

button {
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
  border: none;
  background-color: #007BFF;
  color: white;
  border-radius: 5px;
}

button:hover {
  background-color: #0056b3;
}

Enter fullscreen mode Exit fullscreen mode

JS:

const board = document.getElementById('game-board');
const scoreDisplay = document.getElementById('score');
const startRestartButton = document.getElementById('start-restart');

const gridSize = 20;
const boardSize = gridSize * gridSize;
let snake = [42, 41, 40]; // Starting position
let direction = 1; // Moving right
let nextDirection = 1; // Buffer for next direction
let food = null;
let score = 0;
let intervalId = null;
const speed = 100;

function createBoard() {
  board.innerHTML = '';
  for (let i = 0; i < boardSize; i++) {
    const cell = document.createElement('div');
    cell.classList.add('cell');
    board.appendChild(cell);
  }
}

function updateBoard() {
  const cells = document.querySelectorAll('.cell');
  cells.forEach(cell => cell.classList.remove('snake', 'food'));

  snake.forEach(index => cells[index].classList.add('snake'));
  if (food !== null) cells[food].classList.add('food');
}

function spawnFood() {
  let newFood;
  do {
    newFood = Math.floor(Math.random() * boardSize);
  } while (snake.includes(newFood));
  food = newFood;
}

function moveSnake() {
  const head = snake[0];
  const newHead = getNewHead(head);

  // Check for collisions with itself
  if (snake.includes(newHead)) {
    clearInterval(intervalId);
    alert(`Game Over! Your final score is ${score}.`);
    return;
  }

  // Move snake
  snake.unshift(newHead);
  if (newHead === food) {
    score++;
    scoreDisplay.textContent = `Score: ${score}`;
    spawnFood();
  } else {
    snake.pop();
  }
  direction = nextDirection;
  updateBoard();
}

function getNewHead(head) {
  let newHead = head + nextDirection;

  if (nextDirection === 1 && head % gridSize === gridSize - 1) {
    newHead = head - (gridSize - 1); // Wrap to the left
  } else if (nextDirection === -1 && head % gridSize === 0) {
    newHead = head + (gridSize - 1); // Wrap to the right
  } else if (nextDirection === gridSize && head + gridSize >= boardSize) {
    newHead = head % gridSize; // Wrap to the top
  } else if (nextDirection === -gridSize && head - gridSize < 0) {
    newHead = head + boardSize - gridSize; // Wrap to the bottom
  }

  return newHead;
}

function changeDirection(e) {
  const key = e.key;

  // Prevent reversing direction
  if (key === 'ArrowUp' && direction !== gridSize) nextDirection = -gridSize;
  if (key === 'ArrowDown' && direction !== -gridSize) nextDirection = gridSize;
  if (key === 'ArrowLeft' && direction !== 1) nextDirection = -1;
  if (key === 'ArrowRight' && direction !== -1) nextDirection = 1;
}

function startGame() {
  snake = [42, 41, 40];
  direction = 1;
  nextDirection = 1;
  score = 0;
  scoreDisplay.textContent = `Score: ${score}`;
  spawnFood();
  updateBoard();

  if (intervalId) clearInterval(intervalId);
  intervalId = setInterval(moveSnake, speed);
}

createBoard();
startRestartButton.addEventListener('click', startGame);
document.addEventListener('keydown', changeDirection);

Enter fullscreen mode Exit fullscreen mode

You can make changes to above code according to what more creativity you can add.

Try playing here: https://codepen.io/alexya99/pen/EaYbaxo

As a game developer I have worked on many gaming projects. You can review our one of gaming portfolio site such as geometry dash spam full of such web based games.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more