A modern implementation of the classic Tetris game featuring sophisticated mobile touch controls, Firebase leaderboards, and responsive design. Built with HTML5 Canvas and vanilla JavaScript for optimal performance.
tetris/
├── index.html # Main game application
├── cnfg.js # Firebase configuration
├── tetrisInstructions.png # Control instructions image
└── tetrisfavicon.ico # Game favicon
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 32;
const COLORS = {
I: '#43A3B7', // Cyan
O: '#F6BD44', // Yellow
T: '#6F41B1', // Purple
S: '#4CA84C', // Green
Z: '#E14331', // Red
J: '#4D86F7', // Blue
L: '#DC6C33' // Orange
};
const SHAPES = {
I: [[0,0,0,0], [1,1,1,1], [0,0,0,0], [0,0,0,0]],
J: [[1,0,0], [1,1,1], [0,0,0]],
L: [[0,0,1], [1,1,1], [0,0,0]],
O: [[1,1], [1,1]],
S: [[0,1,1], [1,1,0], [0,0,0]],
Z: [[1,1,0], [0,1,1], [0,0,0]],
T: [[0,1,0], [1,1,1], [0,0,0]]
};
Implements fair piece distribution to prevent long droughts:
function fillBag() {
const pieces = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'];
const shuffled = pieces.sort(() => Math.random() - 0.5);
bag.push(...shuffled);
}
Efficient boundary and block collision checking:
function isValidMove(piece, x, y) {
for (let row = 0; row < piece.shape.length; row++) {
for (let col = 0; col < piece.shape[row].length; col++) {
if (piece.shape[row][col]) {
const newX = x + col;
const newY = y + row;
if (newX < 0 || newX >= COLS || newY >= ROWS) return false;
if (newY >= 0 && board[newY][newX]) return false;
}
}
}
return true;
}
function clearLines() {
let linesCleared = 0;
for (let y = ROWS - 1; y >= 0; y--) {
if (board[y].every(cell => cell !== 0)) {
board.splice(y, 1);
board.unshift(Array(COLS).fill(0));
linesCleared++;
y++; // Check the same row again
}
}
if (linesCleared > 0) {
score += [100, 250, 500, 1000][linesCleared - 1];
updateScore();
}
}
Sophisticated zone-based touch system:
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
if (y < centerY) {
// Top half - rotate
rotatePiece();
} else if (x < centerX) {
// Bottom-left - move left
movePiece(-1);
// Start continuous movement
touchHoldInterval = setInterval(() => movePiece(-1), 100);
} else {
// Bottom-right - move right
movePiece(1);
touchHoldInterval = setInterval(() => movePiece(1), 100);
}
});
Three-column desktop layout with mobile stack:
@media (max-width: 768px) {
body {
flex-direction: column;
padding: 20px;
}
.game-container {
width: 100%;
height: 85vh;
margin-bottom: 40px;
}
}
Modern aesthetic with rounded corners:
function drawBlock(x, y, color) {
const padding = 1;
const blockX = x * BLOCK_SIZE + padding;
const blockY = y * BLOCK_SIZE + padding;
const blockSize = BLOCK_SIZE - padding * 2;
const radius = 4;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(blockX + radius, blockY);
ctx.arcTo(blockX + blockSize, blockY, blockX + blockSize, blockY + blockSize, radius);
// ... additional rounded corner drawing
ctx.fill();
}
Fading overlay system for mobile users:
if (window.innerWidth <= 768 && !gameOver) {
const gameTime = Date.now() - gameStartTime;
const fadeOutStart = 3000; // Start fading after 3 seconds
const fadeOutDuration = 2000; // Fade over 2 seconds
let opacity = 1;
if (gameTime > fadeOutStart) {
opacity = Math.max(0, 1 - (gameTime - fadeOutStart) / fadeOutDuration);
}
if (opacity > 0) {
// Draw control hint overlay
drawControlHints(opacity);
}
}
firebase.initializeApp(firebaseConfig);
const leaderboardRef = firebase.database().ref('leaderboard');
leaderboardRef.orderByChild('score').on('value', (snapshot) => {
const entries = {};
snapshot.forEach((child) => {
const entry = child.val();
const name = entry.name;
const score = entry.score;
// Keep highest score per player
if (!entries[name] || score > entries[name]) {
entries[name] = score;
}
});
// Sort and display leaderboard
const sorted = Object.entries(entries)
.map(([name, score]) => ({ name, score }))
.sort((a, b) => b.score - a.score);
updateLeaderboardDisplay(sorted);
});
function handleGameOver() {
if (score >= 1000) {
const name = prompt("Game Over! Enter your name (max 5 characters):");
if (name && name.trim()) {
const trimmedName = name.trim().substring(0, 5);
// Simple profanity filter
const bannedWords = ['cock','balls','pussy','fuck','shit','cunt','bitch','penis','ass','piss','slut','whore'];
const nameLC = trimmedName.toLowerCase();
const hasBanned = bannedWords.some(word => nameLC.includes(word));
if (!hasBanned || trimmedName === 'Logan W') {
firebase.database().ref('leaderboard').push({
name: trimmedName,
score: score,
timestamp: firebase.database.ServerValue.TIMESTAMP
});
}
}
}
}
sorted.forEach((entry, index) => {
const li = document.createElement('li');
if (index === 0) {
li.textContent = `1st | ${entry.name} - ${entry.score} 🥇`;
} else if (index === 1) {
li.textContent = `2nd | ${entry.name} - ${entry.score} 🥈`;
} else if (index === 2) {
li.textContent = `3rd | ${entry.name} - ${entry.score} 🥉`;
} else {
li.textContent = `${index + 1}. ${entry.name} - ${entry.score}`;
}
list.appendChild(li);
});
function gameLoop(time = 0) {
if (gameOver) return;
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > 1000) {
dropPiece();
dropCounter = 0;
}
draw();
requestAnimationFrame(gameLoop);
}
This Tetris implementation represents a sophisticated modern take on the classic game, featuring advanced mobile controls, real-time leaderboards, and optimized performance. The combination of faithful gameplay mechanics with contemporary web technologies creates an engaging and accessible gaming experience across all devices.
Technical Rating: 9.0/10