I have some bugs in my Tetris game, please help me fix them and give some suggestions and then implement to make the game more user friendly. And finally see if there is any missing features or any good features that can be added.

Below is a Tetris game implemented in JavaScript with a simple but functional GUI using HTML5 and CSS.

Sure! Below is a Tetris game implemented in JavaScript with a simple but functional GUI using HTML5 and CSS. It includes a game board, falling tetrominoes, and basic controls for moving and rotating the pieces. You can expand on this further with more styles, animations, and features.

### HTML (index.html)
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Enhanced Tetris</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="container">
        <canvas id="gameBoard" width="300" height="600"></canvas>
        <div class="info">
            <h1 class="game-title">Tetris</h1>
            <p>Use Arrow Keys to move and rotate the pieces.</p>
            <p>Score: <span id="score">0</span></p>
            <p>Level: <span id="level">1</span></p>
            <canvas id="nextPiece" width="120" height="120"></canvas>
            <canvas id="holdPiece" width="120" height="120"></canvas>
            <button id="holdButton" style="display: none;">Hold</button> <!-- Initially hidden -->
            <button id="playButton">Play</button>
        </div>
    </div>

    <!-- Game Over Modal -->
    <div id="gameOverModal" class="modal">
        <div class="modal-content">
            <h2>Game Over</h2>
            <p>Your Score: <span id="finalScore">0</span></p>
            <p>Your Level: <span id="finalLevel">1</span></p>
            <button id="restartButton">Restart</button>
            <button id="exitButton">Exit</button>
        </div>
    </div>

    <!-- Audio Elements -->
    <audio id="moveSound" src="sounds/move.mp3"></audio>
    <audio id="rotateSound" src="sounds/rotate.mp3"></audio>
    <audio id="clearSound" src="sounds/clear.mp3"></audio>
    <audio id="gameOverSound" src="sounds/gameover.mp3"></audio>
    <audio id="modalAppearSound" src="sounds/modalAppear.mp3"></audio> <!-- Optional -->
    <script src="tetris.js"></script>
</body>
</html>
```

### CSS (styles.css)
```css
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    background: linear-gradient(135deg, #1e1e1e, #2c2c2c);
    color: white;
    overflow: hidden;
    animation: backgroundMove 10s linear infinite;
    background-size: 400% 400%;
}

@keyframes backgroundMove {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
}

.container {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: 20px;
}

canvas {
    background-color: #111;
    border: 4px solid #3e3e3e;
    box-shadow: 0 0 20px rgba(0,0,0,0.5);
}

.info {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.game-title {
    font-family: 'Press Start 2P', cursive;
    font-size: 2rem;
    margin-bottom: 10px;
    animation: titleEntrance 1s ease-out;
}

@keyframes titleEntrance {
    from {
        transform: translateY(-50px);
        opacity: 0;
    }
    to {
        transform: translateY(0);
        opacity: 1;
    }
}

button {
    background: linear-gradient(145deg, #ff4081, #ff79a1);
    color: white;
    border: none;
    padding: 12px 24px;
    font-size: 1rem;
    cursor: pointer;
    margin-top: 10px;
    border-radius: 8px;
    box-shadow:  5px 5px 15px #bebebe,
                -5px -5px 15px #ffffff;
    transition: all 0.3s ease;
}

button:hover {
    transform: translateY(-2px);
    box-shadow:  7px 7px 20px #bebebe,
                -7px -7px 20px #ffffff;
}

button:active {
    transform: translateY(0px);
    box-shadow: inset 2px 2px 5px #bebebe,
                inset -2px -2px 5px #ffffff;
}

#score, #level, #finalScore, #finalLevel {
    font-size: 1.2rem;
    font-weight: bold;
}

.modal {
    display: none; /* Hidden by default */
    position: fixed; /* Stay in place */
    z-index: 10; /* Sit on top */
    left: 0;
    top: 0;
    width: 100%; /* Full width */
    height: 100%; /* Full height */
    overflow: auto; /* Enable scroll if needed */
    background: rgba(0, 0, 0, 0.8); /* Black w/ opacity */
    justify-content: center;
    align-items: center;
    animation: fadeIn 0.5s;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

.modal-content {
    background-color: #2c2c2c;
    padding: 30px 20px;
    border-radius: 10px;
    text-align: center;
    animation: slideDown 0.5s;
    position: relative;
    overflow: hidden;
}

@keyframes slideDown {
    from { transform: translateY(-50px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}

.modal-content h2 {
    font-family: 'Press Start 2P', cursive;
    font-size: 2rem;
    margin-bottom: 15px;
    animation: flash 1s infinite;
}

@keyframes flash {
    0%, 50%, 100% { opacity: 1; }
    25%, 75% { opacity: 0.5; }
}

.modal-content p {
    font-size: 1.2rem;
    margin: 10px 0;
}

.modal-content button {
    width: 120px;
    margin: 15px 10px 0 10px;
    padding: 10px;
}

/* Responsive Adjustments */
@media (max-width: 600px) {
    .container {
        flex-direction: column;
        align-items: center;
    }
    
    .modal-content {
        width: 80%;
    }
}
```

### JavaScript (tetris.js)
```javascript
const canvas = document.getElementById('gameBoard');
const ctx = canvas.getContext('2d');
const playButton = document.getElementById('playButton');
const scoreDisplay = document.getElementById('score');
const levelDisplay = document.getElementById('level');
const nextPieceCanvas = document.getElementById('nextPiece');
const nextCtx = nextPieceCanvas.getContext('2d');
const holdPieceCanvas = document.getElementById('holdPiece');
const holdCtx = holdPieceCanvas.getContext('2d');
const holdButton = document.getElementById('holdButton'); // Ensure this line is present

// Modal Elements
const gameOverModal = document.getElementById('gameOverModal');
const finalScoreDisplay = document.getElementById('finalScore');
const finalLevelDisplay = document.getElementById('finalLevel');
const restartButton = document.getElementById('restartButton');
const exitButton = document.getElementById('exitButton');

const moveSound = document.getElementById('moveSound');
const rotateSound = document.getElementById('rotateSound');
const clearSound = document.getElementById('clearSound');
const gameOverSound = document.getElementById('gameOverSound');
const modalAppearSound = document.getElementById('modalAppearSound'); // Optional

const ROWS = 20;
const COLS = 10;
const BLOCK_SIZE = 30;
const EMPTY_COLOR = '#111';
const COLORS = ['#FF5733', '#33FF57', '#3357FF', '#FF33A8', '#FF8F33', '#FFD433', '#33FFF6'];

let board = [];
let currentPiece;
let nextPiece = null;
let holdPiece = null;
let score = 0;
let level = 1;
let gameInterval;
let gameSpeed = 500; // initial speed in ms
const SPEED_INCREMENT = 50; // speed increase every level
const LINES_PER_LEVEL = 10;
let canHold = true;
let keyCooldown = false;
const KEY_DELAY = 100; // milliseconds

// Tetromino shapes
const SHAPES = [
    [[1, 1, 1, 1]], // I
    [[1, 1], [1, 1]], // O
    [[0, 1, 1], [1, 1, 0]], // S
    [[1, 1, 0], [0, 1, 1]], // Z
    [[1, 1, 1], [0, 1, 0]], // T
    [[1, 1, 1], [1, 0, 0]], // L
    [[1, 1, 1], [0, 0, 1]]  // J
];

// Create and initialize the board
function createBoard() {
    board = Array.from({ length: ROWS }, () => Array(COLS).fill(EMPTY_COLOR));
}

// Draw a single block at (x, y) with the specified color
function drawBlock(x, y, color) {
    const blockX = x * BLOCK_SIZE;
    const blockY = y * BLOCK_SIZE;

    // Create gradient
    const gradient = ctx.createLinearGradient(blockX, blockY, blockX + BLOCK_SIZE, blockY + BLOCK_SIZE);
    gradient.addColorStop(0, lightenColor(color, 0.3));
    gradient.addColorStop(1, color);

    ctx.fillStyle = gradient;
    ctx.fillRect(blockX, blockY, BLOCK_SIZE, BLOCK_SIZE);

    // Add shadow
    ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
    ctx.shadowBlur = 10;
    ctx.shadowOffsetX = 2;
    ctx.shadowOffsetY = 2;

    ctx.strokeStyle = '#333';
    ctx.lineWidth = 2;
    ctx.strokeRect(blockX, blockY, BLOCK_SIZE, BLOCK_SIZE);

    // Reset shadow
    ctx.shadowColor = 'transparent';
    ctx.shadowBlur = 0;
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = 0;
}

// Utility function to lighten a color
function lightenColor(color, percent) {
    const num = parseInt(color.replace('#',''),16),
          amt = Math.round(2.55 * percent * 100),
          R = (num >> 16) + amt,
          G = ((num >> 8) & 0x00FF) + amt,
          B = (num & 0x0000FF) + amt;
    return '#' + (
        0x1000000 + 
        (R<255?R<1?0:R:255)*0x10000 + 
        (G<255?G<1?0:G:255)*0x100 + 
        (B<255?B<1?0:B:255)
    ).toString(16).slice(1);
}

// Clear the entire canvas
function clearCanvas() {
    ctx.fillStyle = EMPTY_COLOR;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
}

// Render the board and the current piece
function render() {
    clearCanvas();
    drawBoard();
    if (currentPiece) {
        currentPiece.draw();
    }
}

// Draw the board blocks
function drawBoard() {
    for (let row = 0; row < ROWS; row++) {
        for (let col = 0; col < COLS; col++) {
            if (board[row][col] !== EMPTY_COLOR) {
                drawBlock(col, row, board[row][col]);
            }
        }
    }
}

// Generate a random Tetromino piece
function randomPiece() {
    if (!nextPiece) {
        nextPiece = new Piece(SHAPES[Math.floor(Math.random() * SHAPES.length)], COLORS[Math.floor(Math.random() * COLORS.length)]);
    }
    const piece = nextPiece;
    nextPiece = new Piece(SHAPES[Math.floor(Math.random() * SHAPES.length)], COLORS[Math.floor(Math.random() * COLORS.length)]);
    drawNextPiece();
    return piece;
}

// Piece class representing a Tetromino
class Piece {
    constructor(shape, color) {
        this.shape = shape;
        this.color = color;
        this.x = Math.floor(COLS / 2) - Math.floor(this.shape[0].length / 2);
        this.y = 0;
    }

    // Draw the piece on the canvas
    draw() {
        this.shape.forEach((row, dy) => {
            row.forEach((value, dx) => {
                if (value) {
                    drawBlock(this.x + dx, this.y + dy, this.color);
                }
            });
        });
    }

    // Move the piece by (dx, dy)
    move(dx, dy) {
        if (!this.collides(this.x + dx, this.y + dy, this.shape)) {
            this.x += dx;
            this.y += dy;
            return true;
        }
        return false;
    }

    // Rotate the piece clockwise
    rotate() {
        const rotatedShape = this.shape[0].map((_, i) => this.shape.map(row => row[i]).reverse());
        if (!this.collides(this.x, this.y, rotatedShape)) {
            this.shape = rotatedShape;
            rotateSound.play();
        } else {
            // Attempt to shift piece to the left or right if rotation causes collision
            if (!this.collides(this.x - 1, this.y, rotatedShape)) {
                this.x -= 1;
                this.shape = rotatedShape;
                rotateSound.play();
            } else if (!this.collides(this.x + 1, this.y, rotatedShape)) {
                this.x += 1;
                this.shape = rotatedShape;
                rotateSound.play();
            }
        }
    }

    // Check if the piece collides with the board or boundaries
    collides(x, y, shape) {
        return shape.some((row, dy) => {
            return row.some((value, dx) => {
                let newX = x + dx;
                let newY = y + dy;
                // Allow pieces to spawn above the board
                if (newY < 0) return false;
                return (
                    value &&
                    (newX < 0 || newX >= COLS || newY >= ROWS || board[newY][newX] !== EMPTY_COLOR)
                );
            });
        });
    }

    // Lock the piece into the board
    lock() {
        this.shape.forEach((row, dy) => {
            row.forEach((value, dx) => {
                if (value) {
                    const newY = this.y + dy;
                    const newX = this.x + dx;
                    if (newY >= 0 && newY < ROWS && newX >= 0 && newX < COLS) {
                        board[newY][newX] = this.color;
                    }
                }
            });
        });
    }
}

// Clear completed lines with animation and update the score
function clearLinesWithAnimation(callback) {
    let linesCleared = [];
    for (let row = ROWS - 1; row >= 0; row--) {
        if (board[row].every(cell => cell !== EMPTY_COLOR)) {
            linesCleared.push(row);
        }
    }

    if (linesCleared.length === 0) {
        callback(0);
        return;
    }

    // Animate each cleared line sequentially
    let animationPromise = Promise.resolve();

    linesCleared.forEach(row => {
        animationPromise = animationPromise.then(() => animateLineClear(row));
    });

    animationPromise.then(() => {
        // Remove cleared lines
        linesCleared.forEach(row => {
            board.splice(row, 1);
            board.unshift(Array(COLS).fill(EMPTY_COLOR));
        });

        // Update the score after animation
        if (linesCleared.length > 0) {
            clearSound.play();
            score += linesCleared.length * 10;
            scoreDisplay.textContent = score;
            callback(linesCleared.length);
        }
    });
}

// Function to animate the line clearing with a fading effect
function animateLineClear(row) {
    return new Promise((resolve) => {
        const totalFrames = 20;
        let currentFrame = 0;
        const originalColors = board[row].slice(); // Store original colors

        function animate() {
            if (currentFrame < totalFrames) {
                const fadeFactor = currentFrame / totalFrames;
                board[row].forEach((_, col) => {
                    board[row][col] = fadeColor(originalColors[col], fadeFactor);
                });

                render();
                currentFrame++;
                requestAnimationFrame(animate);
            } else {
                // Line has been cleared, proceed
                resolve();
            }
        }

        animate();
    });
}

// Utility function to fade a color based on the fade factor (0 to 1)
function fadeColor(color, factor) {
    // Convert HEX to RGB
    let r, g, b;
    if (color.startsWith('#')) {
        const hex = color.substring(1);
        const bigint = parseInt(hex, 16);
        r = (bigint >> 16) & 255;
        g = (bigint >> 8) & 255;
        b = bigint & 255;
    } else if (color.startsWith('rgb')) {
        const rgbValues = color.match(/\d+/g);
        r = parseInt(rgbValues[0]);
        g = parseInt(rgbValues[1]);
        b = parseInt(rgbValues[2]);
    }

    // Calculate faded color towards EMPTY_COLOR (#111)
    const targetR = 17; // HEX for #111
    const targetG = 17;
    const targetB = 17;

    const newR = Math.floor(r + (targetR - r) * factor);
    const newG = Math.floor(g + (targetG - g) * factor);
    const newB = Math.floor(b + (targetB - b) * factor);

    return `rgb(${newR}, ${newG}, ${newB})`;
}

// The main game loop that updates the game state
function gameLoop() {
    if (!currentPiece.move(0, 1)) {
        currentPiece.lock();
        canHold = true; // Reset hold ability
        clearInterval(gameInterval);
        
        // Initiate animation and then proceed
        clearLinesWithAnimation((cleared) => {
            if (cleared > 0) {
                if (Math.floor(score / (LINES_PER_LEVEL * 10)) + 1 > level) {
                    levelUp();
                }
            }
            if (board[0].some(cell => cell !== EMPTY_COLOR)) {
                gameOver();
                return;
            }
            currentPiece = randomPiece();
            // Resume the game loop
            gameInterval = setInterval(gameLoop, gameSpeed);
        });

    }
    render();
}

// Handle game over state
function gameOver() {
    clearInterval(gameInterval);
    gameOverSound.play();
    modalAppearSound.play(); // Optional
    
    // Update final score and level in the modal
    finalScoreDisplay.textContent = score;
    finalLevelDisplay.textContent = level;
    
    // Display the Game Over modal
    gameOverModal.style.display = 'flex';
    
    // Hide the Hold button
    holdButton.style.display = 'none';
}

// Handle user input for controlling the Tetromino
document.addEventListener('keydown', (e) => {
    // Prevent input if modal is active
    if (gameOverModal.style.display === 'flex') return;
    
    if (!currentPiece || keyCooldown) return;
    let moved = false;
    switch (e.key) {
        case 'ArrowLeft':
            moved = currentPiece.move(-1, 0);
            if (moved) moveSound.play();
            break;
        case 'ArrowRight':
            moved = currentPiece.move(1, 0);
            if (moved) moveSound.play();
            break;
        case 'ArrowDown':
            moved = currentPiece.move(0, 1);
            if (moved) moveSound.play();
            break;
        case 'ArrowUp':
            currentPiece.rotate();
            // Rotation sound is handled inside the rotate method
            moved = true;
            break;
    }
    if (moved) {
        render();
        keyCooldown = true;
        setTimeout(() => { keyCooldown = false; }, KEY_DELAY);
    }
});

// Start or restart the game
function startGame() {
    createBoard();
    currentPiece = randomPiece();
    score = 0;
    level = 1;
    scoreDisplay.textContent = score;
    levelDisplay.textContent = level;
    holdPiece = null;
    drawHoldPiece();
    playButton.style.display = 'none';
    clearInterval(gameInterval);
    gameSpeed = 500; // Reset game speed
    gameInterval = setInterval(gameLoop, gameSpeed);
    canHold = true;
    keyCooldown = false;
    
    // Reset and hide the Game Over modal if it's currently displayed
    gameOverModal.style.display = 'none';
    
    // Show the Hold button
    holdButton.style.display = 'inline-block';
    
    render();
}

// Play button event listener
playButton.addEventListener('click', startGame);

// Restart button in the modal
restartButton.addEventListener('click', () => {
    startGame();
});

// Exit button in the modal
exitButton.addEventListener('click', () => {
    // Define what "exit" should do. For this example, we'll reload the page.
    window.location.reload();
});

// Hold feature implementation
holdButton.addEventListener('click', () => {
    if (!canHold || !currentPiece) return;
    if (holdPiece) {
        [holdPiece, currentPiece] = [currentPiece, holdPiece];
        currentPiece.x = Math.floor(COLS / 2) - Math.floor(currentPiece.shape[0].length / 2);
        currentPiece.y = 0;
    } else {
        holdPiece = currentPiece;
        currentPiece = randomPiece();
    }
    drawHoldPiece();
    drawNextPiece();
    canHold = false;
    render();
});

// Draw the next piece in the preview canvas
function drawNextPiece() {
    nextCtx.fillStyle = EMPTY_COLOR;
    nextCtx.fillRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height);
    if (nextPiece) {
        nextPiece.shape.forEach((row, dy) => {
            row.forEach((value, dx) => {
                if (value) {
                    nextCtx.fillStyle = nextPiece.color;
                    nextCtx.fillRect(dx * 30, dy * 30, 30, 30);
                    nextCtx.strokeStyle = '#333';
                    nextCtx.strokeRect(dx * 30, dy * 30, 30, 30);
                }
            });
        });
    }
}

// Draw the hold piece
function drawHoldPiece() {
    holdCtx.fillStyle = EMPTY_COLOR;
    holdCtx.fillRect(0, 0, holdPieceCanvas.width, holdPieceCanvas.height);
    if (holdPiece) {
        holdPiece.shape.forEach((row, dy) => {
            row.forEach((value, dx) => {
                if (value) {
                    holdCtx.fillStyle = holdPiece.color;
                    holdCtx.fillRect(dx * 30, dy * 30, 30, 30);
                    holdCtx.strokeStyle = '#333';
                    holdCtx.strokeRect(dx * 30, dy * 30, 30, 30);
                }
            });
        });
    }
}

// Level up function
function levelUp() {
    clearInterval(gameInterval);
    level++;
    levelDisplay.textContent = level;
    gameSpeed = Math.max(100, gameSpeed - SPEED_INCREMENT);
    gameInterval = setInterval(gameLoop, gameSpeed);
}

// Initialize hold and next piece displays
function initializeDisplays() {
    drawNextPiece();
    drawHoldPiece();
}

// Initial setup
initializeDisplays();
```