JavaScript Snake Game Tutorial

In this tutorial, you will learn how to make classic javascript snake game using HTML, CSS and vanilla JavaScript.

Everyone remembers that retro game. It’s quite easy and cool.

And first of all, I want to show you the result. I uploaded it to my site.

In this tutorial, I will not show you all pieces of javascript snake code. Only the most important parts.

You can find the full source code on the GitHub.

How Complicated to Write JavaScript Snake Game?

Do you think it’s complicated? Nope. Absolutely! I spent just 4 hours to do that.

I’m not professional javascript developer. I searched in google many times…

Read my guide, clone javascript snake source code and you will write it for 1 hour.

Let’s begin…


We need 3 files:

  • index.html
  • snake.css
  • snake.js

snake.css and snake.js keep empty for a moment.

index.html includes references to these files and div container for our board.

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <title>My JavaScript Snake</title>
    <link type="text/css" rel="stylesheet" href="snake.css">
<div id="container"></div>
<script src="snake.js"></script>

Create a Board

I want to specify the number of columns and rows for my board, so I created a Board with a render method.

function Board(containerId, rowsCount, colsCount) {
    this.containerId = containerId;
    this.rowsCount = rowsCount;
    this.colsCount = colsCount;
    this.render = function () {
        var html = "<div class='snake-board'><table>";
        for (var i = 0; i < this.rowsCount; i++) {
            html += "<tr id='row-" + i + "' class='row'>";
            for (var j = 0; j < this.colsCount; j++) {
                html += "<td id='" + cellId(i, j) + "' class='col'></td>";
            html += "</tr>"
        html += "</table></div>";
        document.getElementById(containerId).innerHTML = html;
function cellId(row, col) {
    return "snake-board-" + row + "-" + col;

The logic is simple.

We are iterating though rowsCount and colsCount and creating HTML table and add it to the DOM inside of the container.

Let’s take a look that we’re generating unique ids for each cell. We need to convenient and fast way to access it.

And we need to add some styles:

.snake-board table {
    border-collapse: collapse;
.snake-board td {
    width: 20px;
    height: 20px;
    border: 0;
.snake-board .col {
    background-color: yellow;

Add Snake and Fruit to the Board

At the start point, snake and fruit is an only one cell with a different style.

So the only 3 things we have to do here:

Select snake start point, select an empty cell for a fruit and add styles for it.

function Snake(board, speed, head) {
    this.head = head;
    this.tail = head;
    this.cells = [head];
    this.board = board;
    var $snake = this;
    this.start = function () {
        addClass(document.getElementById(cellId(head.row, head.col)), "snake");
        addFruitToBoard(board, this);
function addFruitToBoard(board, snake) {
    do {
        var row = Math.floor(Math.random() * board.rowsCount);
        var col = Math.floor(Math.random() * board.colsCount);
    } while (snake.containsCell(row, col));
    addClass(document.getElementById(cellId(row, col)), "fruit");
function addClass(element, className) {

I’ve created a Snake. I want to store an information about all cells, head, and tail and move it on the board.

And some styles.

.snake-board .snake {
    background-color: blue;
.snake-board .fruit {
    background-color: red;

Make Snake Moving

The trick is simple – you should change cells styling in the interval.

Each cell has a direction. So we know what is the next cell.

So what’s necessary:

  • We have to take each snake cell and move it (remove a CSS class from current cell and add it to the next one) according to its direction.
  • We should determine if the next cell is a fruit and if yes (snake should grow up).
  • We should determine that snake is out of the board (the game is over).
  • We should determine if snakehead has a collision with one of other snake elements (the game is over).
function Snake(board, speed, head) {
    this.head = head;
    this.tail = head;
    this.cells = [head];
    this.board = board;
    var $snake = this;
    this.start = function () {
        addClass(document.getElementById(cellId(head.row, head.col)), "snake");
        addFruitToBoard(board, this);
        var intervalId = setInterval(function () {
            var newCells = [];
            var headMovingResult = moveHead();
            if (!headMovingResult) {
            if ($snake.cells.length > 1) {
                newCells.push.apply(newCells, moveCellsAfterHead($snake));
            $snake.head = newCells[0];
            $snake.tail = newCells[newCells.length - 1];
            $snake.cells = newCells;
            if (headMovingResult.fruit) {
                addFruitToBoard($snake.board, $snake);
        }, speed);
        function moveHead() {
            var newHead = $;
            var nextHeadElement = document.getElementById(cellId(newHead.row, newHead.col));
            if (!nextHeadElement || nextHeadElement.classList.contains("snake")) {
                return null;
            var fruit = nextHeadElement.classList.contains("fruit");
            if (fruit) {
                removeClass(nextHeadElement, "fruit");
            removeClass(document.getElementById(cellId($snake.head.row, $snake.head.col)), "snake");
            addClass(nextHeadElement, "snake");
            return {newHead: newHead, fruit: fruit};
        function moveCellsAfterHead($snake) {
            var newCells = [];
            for (var i = 1; i < $snake.cells.length; i++) {
                var currentCell = $snake.cells[i];
                removeClass(document.getElementById(cellId(currentCell.row, currentCell.col)), "snake");
                var nextCell = $snake.cells[i - 1];
                addClass(document.getElementById(cellId(nextCell.row, nextCell.col)), "snake");
            return newCells;
    this.setHeadDirection = function (keywordDirection) {
        this.head.direction = keywordDirection;
        this.cells[0].direction = keywordDirection;
    this.grow = function () {
        var newTail = this.tail.prev();
        this.tail = newTail;
    this.containsCell = function (row, col) {
        for (var i = 0; i < this.cells.length; i++) {
            var cell = this.cells[i];
            if (cell.row === row && cell.col === col) {
                return true;
        return false;

Mobile Devices Support

Diego Pascual Formoso was so kind and added mobile device support.

He said:

Only took me five minutes, but hopefully it works like a charm

So the first step is to add hammer library to the bottom of index.html:

<script src=""></script>

and add a listener for swipe event and handle it in the same way as keydown event:

function addSwipeListener(snake) {
    var board = document.getElementsByClassName("snake-board")[0];
    var hammer = new Hammer(board);
    hammer.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
    hammer.on('swipe', function(event, arg1, arg2) {
      var keywordDirection = direction(event.direction);
      if (keywordDirection && isAllowedDirection(snake.head.direction, keywordDirection)) {


My JavaScript Snake is working on Chrome browser. I’m a lazy man and I didn’t perform testing under other browsers.

The main goal was to show you main steps how to do write your own snake.

If you find bugs, want to improve, refactor somehow my code – PRs are always welcome.

If you don’t understand something – ask me.


Leave a Comment