Sebastian Poręba's blog

3D Tetris with Three.js tutorial – part 5

In the fifth part of tutorial we add some final polish to the game.

Completed slices and score counting

This function will be quite long but simple. To check if a slice is completed I calculate the maximum number of occupied fields and check every slice (moving on z-axis) if it’s full. This way I can change size of the board and this function should be still working. Try to think about all your functions in such way – if something may ever change, make your code flexible.

Tetris.Board.checkCompleted = function() {
  var x,y,z,x2,y2,z2, fields = Tetris.Board.fields;
  var rebuild = false;

  var sum, expected = fields[0].length*fields.length, bonus = 0;

  for(z = 0; z < fields[0][0].length; z++) {
    sum = 0;
    for(y = 0; y < fields[0].length; y++) {
      for(x = 0; x < fields.length; x++) {
        if(fields[x][y][z] === Tetris.Board.FIELD.PETRIFIED) sum++;
    // to be continued

When the slice is full, we should remove it and shift all the following slices. To make sure that we don’t skip a shifted slice we decrease z once. To make the game more interesting, bonus points are granted if there are multiple slices completed at once.

    if(sum == expected) {
      bonus += 1 + bonus; // 1, 3, 7, 15...

      for(y2 = 0; y2 < fields[0].length; y2++) {
        for(x2 = 0; x2 < fields.length; x2++) {
          for(z2 = z; z2 < fields[0][0].length-1; z2++) {
            Tetris.Board.fields[x2][y2][z2] = fields[x2][y2][z2+1]; // shift
          Tetris.Board.fields[x2][y2][fields[0][0].length-1] = Tetris.Board.FIELD.EMPTY;
      rebuild = true;
  if(bonus) {
    Tetris.addPoints(1000 * bonus);

Now, even though we dealt with board information, we still have to make changes to Three.js geometries. We couldn’t do it in previous loop as it could rebuild the geometries twice or even more if multiple slices were completed at once. This loop checks every Tetris.Board.fields with corresponding Tetris.staticBlocks adding and removing geometries where needed.

  if(rebuild) {
    for(var z = 0; z < fields[0][0].length-1; z++) {
      for(var y = 0; y < fields[0].length; y++) {
        for(var x = 0; x < fields.length; x++) {
          if(fields[x][y][z] === Tetris.Board.FIELD.PETRIFIED && !Tetris.staticBlocks[x][y][z]) {
          if(fields[x][y][z] == Tetris.Board.FIELD.EMPTY && Tetris.staticBlocks[x][y][z]) {
            Tetris.staticBlocks[x][y][z] = undefined;

Audio API

Adding audio is very simple with HTML5. Let’s start with adding <audio> elements to index.html.

<audio id="audio_theme" src="music/tetris.mp3" preload="auto"></audio>
<audio id="audio_move" src="music/move.mp3" preload="auto"></audio>
<audio id="audio_collision" src="music/collision.mp3" preload="auto"></audio>
<audio id="audio_gameover" src="music/gameover.mp3" preload="auto"></audio>
<audio id="audio_score" src="music/cash.mp3" preload="auto"></audio>

Using these files in JS is also easy. First create an object to store your sounds:

// before Tetris.init()
Tetris.sounds = {};

To call Audio API we have to retrieve these DOM elements.

// in Tetris.init()
Tetris.sounds["theme"] = document.getElementById("audio_theme");  
Tetris.sounds["collision"] = document.getElementById("audio_collision");  
Tetris.sounds["move"] = document.getElementById("audio_move");  
Tetris.sounds["gameover"] = document.getElementById("audio_gameover");  
Tetris.sounds["score"] = document.getElementById("audio_score");  

There are numerous methods and you can create you own audio player but for our purposes play() and pause() is enough. You can probably guess where you should add the music :)

  • Tetris.sounds[“theme”].play() – in Tetris.init(), right after initialization of sound object.
  • Tetris.sounds[“theme”].pause() – in Tetris.start().
  • else {Tetris.sounds[“move”].play();} – in Tetris.Block.move(), if there is no ground collision.
  • Tetris.sounds[“collision”].play(); – in Tetris.Block.move(), if there is a ground collision.
  • Tetris.sounds[“score”].play(); – in Tetris.addPoints().
  • Tetris.sounds[“gameover”].play(); – in Tetris.Block.generate(), where we test for the lost game.

The end

That’s all folks! Our Tetris is fully functional now. I hope it was a fun way to learn Three.js. There are many topics like advanced geometries, shaders, lights, skeletal animation, etc., that were not covered here. I just wanted to show that to create a game they’re not always needed. Actually, the best fun I’ve ever had was with games like Tetris, where gameplay was not directly related to the graphics quality. Obviously, great games often comes with great looks, but if you are not having fun from the first to the last minute, not even the best graphics can help.

If you liked the series, please share and comment. If you want to learn more, you should probably use pure WebGL from now on. You may start with this tutorial. Check also "Building the game" by Brandon Jones.

After this tutorial you should:

  • Know how to detect and manage completed slices.
  • Know basics of Audio API.
  • Know how to have fun.
  • Start writing your own game.

Grab source from github

If you have trouble with any of these, check tutorial again or ask a question in the comments below.

2 Responses so far.

  1. Stargazer says:

    Thanks for this super awesome and comprehensible guide – you rock! Greetings from Slovenia =)

  2. Stargazer says:

    btw..if i wanted to clear only one row, how could I do that?

  • RSS
  • Facebook
  • Twitter

FAQ about Wordpress

This came as a surprise for me but gMap is ...

gMap 3.3.3 released

It was a looong time since I last visited gMap. ...

Talks for Google Dev

Two new slide decks appeared in lectures tab. This time with ...

Talks and lectures w

Every now and then I spend a weekend watching various ...

3D Tetris with Three

In the fifth part of tutorial we add some final ...

FAQ about Wordpress

This came as a surprise for me but gMap is ...

gMap 3.3.0 released

Christmas came early! New version of gMap is ready!

Lecture for GTUG: Ja

Today I gave a lecture for GTUG Krakow about optimizations in ...

Unit testing for jQu

In part 1 I described basics of unit testing in ...

Unit testing for jQu

In part 1 I described some basic concepts behind unit ...