This is a minimal TypeScript/React implementation of Tetris using HTML5 Canvas for rendering. The game features standard tetromino pieces, line clearing, and three distinct game states.
Live Demo: tkhquang.github.io/react-minimal-tetris/
The application follows a modular structure with clear separation of concerns. The Board class serves as the core data structure using a sparse Map for efficient storage. The useGameLogic hook manages all game state and timing, while React components handle rendering through Canvas.
The game operates in three states:
- Falling: A piece is actively dropping, responding to player input and gravity
- Flashing: Animation plays when lines are complete (18 frames over 900ms)
- GameOver: The board is full and awaits restart
The game uses a sparse storage approach where only occupied cells are tracked in a Map. Empty cells are not stored, making the structure memory-efficient. Positions are keyed as strings (e.g., "3,5") with color values.
- Arrow keys move pieces left, right, and down
- Up arrow rotates counter-clockwise
- Space rotates clockwise
- Enter restarts when game over
Every movement checks that all piece blocks remain within bounds (0 ≤ x < 8, 0 ≤ y < 20) and don't overlap occupied cells.
Rotation uses a wall-kick algorithm: first attempting rotation at current position, then trying one cell left if blocked.
The game implements two timing options:
- RequestAnimationFrame (default): Smooth 60fps rendering with accumulated time tracking
- SetInterval (alternative): Fixed 16ms updates, available as commented code
Pieces fall automatically every 700ms, with immediate response to player input.
stateDiagram-v2 [*] --> Falling: Initialize state Falling { [*] --> Active Active --> Active: Move/Rotate Active --> Checking: Gravity/Down Checking --> Active: Valid Checking --> Locking: Invalid Locking --> [*]: Check Lines } Falling --> Flashing: Lines Complete Falling --> GameOver: Cannot Place state Flashing { [*] --> Animating: 18 frames Animating --> [*]: Clear Lines } Flashing --> Falling: New Piece GameOver --> Falling: Enter Key flowchart TD Start([Frame Start]) Input{Input?} Time{Time Update?} State{State} Start --> Input Input --> Time Time --> State State -->|Falling| Movement[Process Movement] State -->|Flashing| Animation[Update Animation] State -->|GameOver| Wait[Wait for Enter] Movement --> Collision{Valid?} Collision -->|Yes| Render Collision -->|No| Lock[Lock & Check Lines] Animation --> Render Wait --> Render Lock --> Render Render --> Canvas[Draw to Canvas] Canvas --> Start The Canvas renderer draws each cell as nested rectangles for a 3D effect. The outer rectangle shows the piece color, while a smaller inner rectangle uses a darker shade. Visual effects include white overlays for flashing lines and dark overlays for game over state.
To run this Tetris implementation on your local machine, follow these steps:
-
Clone the repository from GitHub:
git clone https://github.com/tkhquang/react-minimal-tetris cd react-minimal-tetris -
Install dependencies
yarn install
-
Start the development server:
yarn dev
-
Open the game in your browser at
http://localhost:5173(Vite's default port)
The game will start automatically once loaded. Use the arrow keys to control pieces, Space/Up for rotation, and Enter to restart when the game is over.
This implementation was restructured and documented with assistance from Claude 3.7, maintaining the minimal approach while adding professional organization and comprehensive documentation.
This React/TypeScript port is based on the original Rust implementation by Dan Aloni, licensed under MIT.
