Building a Tetris Game with Javascript

Welcome to my first post, what better way to start with a super complex but fun project. Before I start, I’d like to give credit to the original creator of this game – Pontus. Feel free to visit his Github and Youtube Channel for more of his projects and videos.

My intent is to complete this project and provide an unbiased review.

Now, a few things to note before we get started. This project assumes you are armed with ES6 and functional programming knowledge, besides this the directory structure is pretty straightforward. The video itself is a good 51 minutes and 34 seconds, so buckle up, this will be quite the ride.

The markup itself is super simple:

<html>
  <head>
    <title>Tetris</title>
  </head>
  <body>
    <canvas id="tetris" width="240" height="400"></canvas>
    <script src="tetris.js"></script>
  </body>
</html>

And the first few lines of Javascript:

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

context.fillStyle = '#000000';
context.fillRect = (0, 0, canvas.width, canvas.height);

The Canvas Element

Just make sure the canvas element is on your page, this allows access to methods and properties associated with the canvas object. This is basically the basic foundation of the game and a thorough understanding of this fundamental concept is paramount to building anything on canvas.

The fillRect() method

fillRect(x-axis, y-axis, canvas width, canvas height)

This method takes 4 arguments.

  • An integer for the x axis
  • An integer for the y axis
  • The width of the canvas
  • The height of the canvas

The result should look like the following image below. Very simple but foundational concept in constructing a canvas object via javascript.

Tetris Pieces

Yup let’s reference some visuals, all you needed to do is google “tetris pieces” and you get some useful images. I went ahead and snagged one for visual reference.

Setting up the Matrix

Cool about 3 minutes in… Let’s draw a T using a 2 dimensional matrix as a starting point.

const matrix = [
  [0, 0, 0], // determines the center of the piece
  [1, 1, 1],
  [0, 1, 0],
];

matrix.forEach((row, y) => {
  row.forEach((value, x) => {
    if (value !== 0) {
      context.fillStyle = 'red';
      context.fillRect(x, y, 1, 1);
    }
  });
});

Will result in the following on your browser. This is a preliminary step so in order to see this one you will have to zoom in.

The forEach() method and the arrow functions (=>) explained

I was curious what the forEach() method was doing here. I lazily assumed that the first parameter was the key, and the second was the value translating into a key, value sequence.

In javascript however, these parameters are flipped when using the forEach() method.


forEach(value, key);

Now with arrow functions – this subject deserves a post of its own so I’ll cover that topic in a future post. To understand arrow functions, you will need a thorough understanding of scope, lexical environments and the ‘this’ keyword.

Wrapping everything into a function

The cool part about functional programming and just about any programming language is eliminating the need for repetition by encapsulating a set of operations within a function scope. The first tetris piece looks good, except it needs to be scaled first.

This will scale the drawn objects 20 times, simple as that.


context.scale(20, 20);

The drawMatrix() function takes care of building each tetris piece, pretty awesome!


function drawMatrix(matrix, offset) {
  matrix.forEach((row, y) => {
    row.forEach((value, x) => {
      if (value !== 0) {
        context.fillStyle = 'red';
        context.fillRect(x + offset.x,
                        y + offset.y,
                        1, 1);
      }
    });
  });
}

drawMatrix(matrix, {x: 5, y: 5});

And Voila! Lookin’ good!

requestAnimationFrame() and a few helper functions

Breaking it down:

  • requestAnimationFrame() – this powerful method makes animation possible, so we will be using this a lot!
  • draw() – The draw() function paints the object itself
  • update() – the update() function is what makes the object actually move. This requires dynamically repainting the object itself, through a method called recursion. An operation which involves calling itself.

Creating the player object

The player object will contain object position and the matrix responsible for creating a tetris piece. So let’s create this first before adding the functions described previously.

Pretty self explanatory, below were creating offsets based on x and y coordinates. 5px from the top and 5px left.The matrix property contains the matrix object.

const player = {
  pos: {
    x: 5,
    y: 5
  },
  matrix: matrix

}

Where is the player object being passed around and where is it being updated?

The draw() function!


function draw() {

  context.fillStyle = '#000000';
  context.fillRect(0, 0, canvas.width, canvas.height);

  drawMatrix(player.matrix, player.pos);
}

Continuously draw our animation with the update() function, defined below. Notice we are defining the update function and then immediately calling itself after the we’ve painted the objects on canvas.

function update() {
  draw();
  requestAnimationFrame(update);
}

We can control the positioning of a tetris piece through the console. We will have to play around with manipulating time in the next section. First, we create the actual object onto our canvas and make them moveable through object methods and properties – in this case the player object.

Then we call the update() function at very bottom of our file.

update();