A guideline-compliant Tetris clone built with vanilla HTML5, CSS3, and ES2023+ JavaScript — no game engine, no framework, no runtime dependencies. Runs in any modern browser, including mobile.
Most "Tetris clones" online are approximations. They skip wall kicks, use a random bag instead of a 7-bag, or handle lock delay incorrectly. The goal here was to implement the actual Tetris guideline — the same specification modern official titles follow — with nothing but the Web platform.
That meant choosing DOM-based rendering over <canvas> to understand the
tradeoffs, writing a real SRS implementation with
separate wall kick tables for I and JLSTZ pieces, and handling every scoring edge case including
back-to-back chains and all-clear bonuses.
The core game logic — board, pieces, scoring, controls — has zero runtime dependencies. GSAP, Three.js, and Motion One are used only for UI animations and background effects, not for anything that touches gameplay.
Full Super Rotation System with separate kick tables for the I-piece and JLSTZ group. All 5 offsets tested in order per attempt.
Every piece appears exactly once per bag of 7. Eliminates the long droughts and streaks that plague naïve random implementations.
500 ms ground timer resets on any successful move or rotation, up to 15 times per piece — enabling guideline finesse play.
Full and mini T-spin detection using the 3-corner rule. Back-to-back multiplier (×1.5) on consecutive Tetris or T-spin clears.
Tap to rotate, swipe to move, swipe down to hard drop, swipe up to hold. Playable without a keyboard on phones and tablets.
Full guideline scoring including all-clear
bonuses. High score persisted to localStorage. Level up every 10 lines; gravity
increases per level.
Keyboard
| Key | Action |
|---|---|
← / → |
Move left / right |
↓ |
Soft drop |
↑ |
Rotate clockwise |
Space |
Hard drop |
C |
Hold piece |
P |
Pause / resume |
Touch
| Gesture | Action |
|---|---|
| Tap | Rotate clockwise |
| Swipe left / right | Move piece |
| Swipe down | Hard drop |
| Swipe up | Hold piece |
All base scores × current level.
Line Clears
| Lines | Base | Name |
|---|---|---|
| 1 | 100 | Single |
| 2 | 300 | Double |
| 3 | 500 | Triple |
| 4 | 800 | Tetris |
T-Spin
| Lines | Base |
|---|---|
| 0 | 400 |
| 1 | 800 |
| 2 | 1200 |
| 3 | 1600 |
Bonuses
| Bonus | Effect |
|---|---|
| Back-to-Back | ×1.5 on consecutive Tetris / T-spin |
| All-Clear | +800 × level |
| Soft drop | +1 per cell |
| Hard drop | +2 per cell |
The game is structured as ES modules — a
Game class for orchestration, with components and services keeping rendering, input,
state, and sound concerns separate.
scripts/
main.js # Game class — game loop, piece actions, orchestration
constants.js # All tunable values (board size, intervals, scoring)
components/
board.js # DOM grid, collision, line clearing, T-spin detection
pieces.js # Tetromino shapes, SRS rotation, wall kick tables
controls.js # Keyboard and touch input
ui.js # Score display, overlays, settings, piece previews
services/
game-state.js # Shared state flags (running / paused / gameover)
score-manager.js# Score, level, B2B chain, localStorage persistence
sound-manager.js# Web Audio clips, volume/mute, memory-safe teardown
utils/
asset-loader.js # Preloads fonts, images, audio before game start
requestAnimationFrame with
delta-time accumulation. Gravity and lock delay tracked via separate counters. No fixed tick
rate.
Flat grid of <div> cells.
Only the ~8 cells touched by the active or ghost piece are updated per frame — no full
repaints.
Vitest + jsdom. Score manager, piece rotation, and board logic covered. No browser required to run the suite.
| Tool | Role |
|---|---|
| Vite | Dev server, HMR, production bundler |
| Vitest | Unit test runner (jsdom environment) |
| GSAP | UI animations |
| Three.js | Background 3D effects |
| Motion One | Declarative element transitions |
| WebFontLoader | Async font loading without FOUT |
| Web Audio API | Background music + SFX (no audio files served separately) |
Core game logic — board, pieces, scoring, controls — has zero runtime dependencies.