Introduction

Tetris is a classic puzzle game that provides an excellent challenge for game development using TypeScript and the HTML5 Canvas API. In this tutorial series, we will build a fully functional Tetris game step by step. By the end of this tutorial, you'll have:

  • A working TypeScript setup that compiles to JavaScript
  • A modular folder structure with separate responsibilities
  • A rendered Tetris grid using the Canvas API

We’ll keep the setup minimal: no frameworks or build tools — just pure TypeScript compiled via tsc and referenced in HTML.


Project Structure

Let’s begin by organizing our project:

tetris-ts/
├── dist/
│   └── main.js           # Output JS from TypeScript
├── src/
│   ├── grid.ts           # Grid rendering and logic
│   └── main.ts           # Entry point
├── index.html            # Game canvas + script reference
├── package.json          # NPM metadata and scripts
├── tsconfig.json         # TypeScript config

Step 1: Initialize NPM and Install TypeScript

On terminal, navigate to your project directory and initialize an NPM project:

mkdir tetris-ts && cd tetris-ts
npm init -y

Install TypeScript as a local development dependency:

npm install typescript --save-dev

Create a tsconfig.json file:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "ES6",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true
  },
  "include": ["src"]
}

Create a build script on your package.json ,under the scripts section:

"scripts": {
  "build": "tsc"
}

You can now compile your TypeScript code by running on your terminal:

npm run build

Step 2: Setting Up HTML

Create a new index.html file:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Tetris in TypeScript</title>
    <style>
      html, body {
        margin: 0;
        padding: 0;
        background: #1c1818;
      }

      canvas {
        border: 1px solid #000;
        background: #000;
        display: block;
        margin: 20px auto;
      }
    </style>
  </head>
  <body>
    <canvas id="tetris"></canvas>
    <script type="module" src="./dist/main.js"></script>
  </body>
</html>

Step 3: Creating the Grid Renderer

Create src/grid.ts to define the Tetris grid and drawing logic:

export const COLS = 10;
export const ROWS = 20;
export const BLOCK_SIZE = 40;

export class Grid {
  private context: CanvasRenderingContext2D;

  constructor(context: CanvasRenderingContext2D) {
    this.context = context;
    this.context.canvas.width = COLS * BLOCK_SIZE;
    this.context.canvas.height = ROWS * BLOCK_SIZE;
    this.context.scale(BLOCK_SIZE, BLOCK_SIZE);
  }

  clear(): void {
    this.context.fillStyle = '#000';
    this.context.fillRect(0, 0, COLS, ROWS);
  }

  drawMatrix(matrix: number[][], offset: { x: number; y: number }): void {
    matrix.forEach((row, y) => {
      row.forEach((value, x) => {
        if (value !== 0) {
          this.context.fillStyle = '#444'; // Placeholder color for grid
          this.context.fillRect(x + offset.x, y + offset.y, 1, 1);
        }
      });
    });
  }
}

Step 4: Entry Point - main.ts

Create src/main.ts as the starting point of the game source:

import { Grid, COLS, ROWS } from './grid';

function createMatrix(w: number, h: number): number[][] {
  const matrix = [];

  for (let i = 0; i < h; i+=1) {
    matrix.push(new Array(w).fill(0));
  }

  return matrix;
}

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

const grid = new Grid(context);
const arena = createMatrix(COLS, ROWS);

function draw() {
  grid.clear();
  grid.drawMatrix(arena, { x: 0, y: 0 });

  requestAnimationFrame(draw);
}

draw();

Step 5: Compile and Run

To compile your code:

npm run build

Then open index.html in your browser. You should see a black canvas with a 10x20 grid (empty, for now). You can optionally mark test blocks in the matrix to see rendering, like so:

arena[0][0] = 1;
arena[0][1] = 1;

Summary and Next Steps

In this tutorial, you:

  • Initialized a TypeScript NPM project
  • Set up a build script for easy compilation
  • Built a reusable Grid class to manage rendering
  • Rendered the Tetris arena using the Canvas API
  • Externalized and modularized the code from the original tetris.html

On our Part 2, we’ll introduce Tetrominoes, movement, and collision detection. You’ll learn how to:

  • Define and draw Tetromino shapes
  • Move them left, right, and down
  • Handle boundary and collision logic cleanly in TypeScript

Stay tuned — we're just getting started!


Read for Part 2?

(Part 2) Building Tetris in TypeScript: Implementing Tetrominoes and Game Logic
In this tutorial, we bring Tetris to life by adding Tetrominoes, player movement, rotation, and collision detection using TypeScript classes. You’ll also implement keyboard controls for interactive gameplay.