JSFiddle - React, Tailwind, and code Playground

CSS

div.tetrisGame {
  position: relative;
  margin: 0;
  padding: 0;
  border: none;
  width: 600px;
  height: 600px;
}

div.tetrisGame div.tetrisLeftPane {
  position: absolute;
  top: 0;
  left: 0;
  width: 300px;
}

div.tetrisLeftPane div.tetrisScore {
  position: absolute;
  top: 150px;
  left: 0;
  font-size: 20pt;
}

div.tetrisLeftPane div.tetrisPreview {
  position: absolute;
  left: 150px;
  width: 120px;  /* 4 cols * 30px */
  height: 120px;  /* 4 rows * 30px */
}

div.tetrisLeftPane div.tetrisUsage {
  position: absolute;
  left: 0;
  top: 300px;
}

div.tetrisLeftPane div.tetrisUsage th,
div.tetrisLeftPane div.tetrisUsage td {
  text-align: left;
}

div.tetrisLeftPane div.tetrisUsage td {
  padding-left: 1em;
}

div.tetrisRightPane {
  position: absolute;
  left: 300px;
  top: 0px;
  width: 300px;  /* 10 cols * 30px */
  height: 600px;  /* 20 rows * 30px */
}

div.tetrisRightPane div.tetrisBoard {
  position: absolute;
  left: 0;
  top: 0;
  width: 300px;  /* 10 cols * 30px */
  height: 600px;  /* 20 rows * 30px */
}

div.tetrisBlock {
  position: absolute;
  width: 29px;
  height: 29px;
  border: 1px solid black;
  background-color: black;
}

div.tetrisBlock.habitated {
  border: 1px solid black;
  background-color: gray;
}

JavaScript

var NUM_ROWS = 20;
var NUM_COLS = 10;
var BLOCK_WIDTH = 30;
var BLOCK_HEIGHT = 30;
var TICK_MS = 400;

var blockPiece = [
  [0, 0, 0, 0],
  [0, 1, 1, 0],
  [0, 1, 1, 0],
  [0, 0, 0, 0]
];

var longPiece = [
  [0, 0, 1, 0],
  [0, 0, 1, 0],
  [0, 0, 1, 0],
  [0, 0, 1, 0]
];

var tPiece = [
  [0, 0, 1, 0],
  [0, 1, 1, 0],
  [0, 0, 1, 0],
  [0, 0, 0, 0]
];

var zlPiece = [
  [0, 0, 0, 0],
  [0, 0, 1, 1],
  [0, 1, 1, 0],
  [0, 0, 0, 0]
];

var zrPiece = [
  [0, 0, 0, 0],
  [0, 1, 1, 0],
  [0, 0, 1, 1],
  [0, 0, 0, 0]
];

var llPiece = [
  [0, 0, 1, 0],
  [0, 0, 1, 0],
  [0, 1, 1, 0],
  [0, 0, 0, 0]
];

var lrPiece = [
  [0, 1, 0, 0],
  [0, 1, 0, 0],
  [0, 1, 1, 0],
  [0, 0, 0, 0]
];

function rotateLeft(piece) {
  return [
    [piece[0][3], piece[1][3], piece[2][3], piece[3][3]],
    [piece[0][2], piece[1][2], piece[2][2], piece[3][2]],
    [piece[0][1], piece[1][1], piece[2][1], piece[3][1]],
    [piece[0][0], piece[1][0], piece[2][0], piece[3][0]]
  ];
}

function rotateRight(piece) {
  return [
    [piece[3][0], piece[2][0], piece[1][0], piece[0][0]],
    [piece[3][1], piece[2][1], piece[1][1], piece[0][1]],
    [piece[3][2], piece[2][2], piece[1][2], piece[0][2]],
    [piece[3][3], piece[2][3], piece[1][3], piece[0][3]]
  ];
}

function intersects(rows, piece, y, x) {
  for (var i = 0; i < 4; i++)
    for (var j = 0; j < 4; j++)
      if (piece[i][j])
        if (y+i >= NUM_ROWS || x+j < 0 || x+j >= NUM_COLS || rows[y+i][x+j])
          return true;
  return false;
}

function apply_piece(rows, piece, y, x) {
  for (var i = 0; i < 4; i++)
    for (var j = 0; j < 4; j++)
      if (piece[i][j])
        rows[y+i][x+j] = 1;
}

function kill_rows(rows) {
  var newRows = [];
  var k = NUM_ROWS;
  for (var i = NUM_ROWS; i --> 0;) {
    for (var j = 0; j < NUM_COLS; j++) {
      if (!rows[i][j]) {
        newRows[--k] = rows[i].slice();
        break;
      }
    }
  }
  for (var i = 0; i < k; i++) {
    newRows[i] = [];
    for (var j = 0; j < NUM_COLS; j++)
     ...