🎮ArcadeLab

Piano Fire

by SonicBear35
1500 lines48.1 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <title>Piano Fire</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            user-select: none;
            -webkit-touch-callout: none;
            -webkit-tap-highlight-color: transparent;
        }

        body {
            background: #0a0a0a;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            color: white;
            overflow: hidden;
            touch-action: none;
            position: fixed;
            width: 100%;
            height: 100%;
        }

        #app {
            width: 100%;
            max-width: 420px;
            height: 100vh;
            max-height: 780px;
            background: linear-gradient(145deg, #0f0a1a, #1a0a2a);
            border-radius: 0px;
            padding: 4px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.9);
            position: relative;
            overflow: hidden;
            border: 2px solid #4a2a6a;
            display: flex;
            flex-direction: column;
            touch-action: none;
        }

        @media (orientation: landscape) {
            #app {
                max-width: 100%;
                max-height: 100vh;
                height: 100vh;
                border-radius: 0;
                flex-direction: row;
                padding: 4px;
            }
            .game-container {
                flex-direction: row !important;
                flex-wrap: wrap !important;
            }
            #gameCanvas {
                height: 100% !important;
                width: 58% !important;
                flex: 1 !important;
            }
            .game-controls {
                flex-direction: column !important;
                width: 38% !important;
                height: 100% !important;
                position: relative !important;
                bottom: auto !important;
                left: auto !important;
                right: auto !important;
                padding: 6px !important;
                display: flex !important;
                justify-content: center !important;
                gap: 4px !important;
                flex-wrap: wrap !important;
            }
            .game-controls button {
                min-width: 50px !important;
                min-height: 40px !important;
                font-size: 9px !important;
            }
            #pauseOverlay {
                width: 58% !important;
                left: 0 !important;
            }
        }

        @media (min-width: 421px) and (orientation: portrait) {
            #app {
                border-radius: 40px;
                height: 780px;
            }
        }

        #loadingScreen {
            display: flex;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: radial-gradient(circle at 50% 30%, #1a0a2a, #0a0a0a);
            z-index: 100;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            border-radius: 0px;
            animation: fadeIn 0.5s ease;
        }

        #loadingScreen .logo {
            font-size: 60px;
            animation: bounce 1s ease infinite;
        }

        #loadingScreen .title {
            font-size: 32px;
            font-weight: 900;
            color: #ff44aa;
            text-shadow: 0 0 30px #ff0088, 0 0 60px #880044;
            margin-top: 5px;
            letter-spacing: 4px;
        }

        #loadingScreen .sub {
            font-size: 14px;
            color: #88dd88;
            margin-top: 5px;
        }

        #loadingScreen .tap-text {
            font-size: 12px;
            color: #557755;
            margin-top: 15px;
            animation: pulse 1.5s ease infinite;
        }

        #loadingScreen .load-bar {
            width: 200px;
            height: 4px;
            background: #1a1a1a;
            border-radius: 4px;
            margin-top: 10px;
            overflow: hidden;
            border: 1px solid #4a2a6a;
        }

        #loadingScreen .load-fill {
            height: 100%;
            width: 0%;
            background: linear-gradient(90deg, #ff44aa, #ff8800);
            border-radius: 4px;
            transition: width 0.3s;
        }

        #loadingScreen .load-percent {
            font-size: 12px;
            color: #ff44aa;
            margin-top: 4px;
        }

        @keyframes fadeIn {
            from { opacity: 0; transform: scale(0.95); }
            to { opacity: 1; transform: scale(1); }
        }

        @keyframes bounce {
            0%, 100% { transform: translateY(0) scale(1); }
            50% { transform: translateY(-15px) scale(1.05); }
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.3; }
        }

        .screen {
            display: none;
            flex-direction: column;
            height: 100%;
            width: 100%;
            position: absolute;
            top: 0;
            left: 0;
            padding: 5px;
            background: linear-gradient(145deg, #0f0a1a, #1a0a2a);
            overflow-y: auto;
        }

        .screen.active {
            display: flex;
        }

        #homeScreen {
            justify-content: center;
            align-items: center;
            gap: 3px;
            background: radial-gradient(circle at 50% 30%, #1a0a2a, #0a0a0a);
        }

        #homeScreen .logo {
            font-size: 34px;
            font-weight: 900;
            color: #ff44aa;
            text-shadow: 0 0 30px #ff0088, 0 0 60px #880044;
            letter-spacing: 3px;
        }

        #homeScreen .sub {
            font-size: 10px;
            color: #88dd88;
            letter-spacing: 4px;
            margin-bottom: 8px;
        }

        .menu-btn {
            background: linear-gradient(145deg, #2a1a3a, #1a0a2a);
            border: 2px solid #4a2a6a;
            color: white;
            padding: 6px 16px;
            border-radius: 60px;
            font-size: 12px;
            font-weight: bold;
            width: 85%;
            text-align: center;
            margin: 2px 0;
            cursor: pointer;
            transition: 0.15s;
            box-shadow: 0 2px 0 #0a0a0a;
            letter-spacing: 1px;
            touch-action: manipulation;
        }

        .menu-btn:active {
            transform: translateY(2px);
            box-shadow: none;
        }

        .menu-btn.pink {
            border-color: #ff44aa;
            background: linear-gradient(145deg, #3a1a4a, #1a0a2a);
        }

        .menu-btn.gold {
            border-color: #ffdd44;
            background: linear-gradient(145deg, #3a3a1a, #1a1a0a);
        }

        .small-btn {
            padding: 2px 6px;
            font-size: 7px;
            width: auto;
        }

        .back-btn {
            background: #2a1a3a;
            border: none;
            color: white;
            padding: 2px 6px;
            border-radius: 30px;
            cursor: pointer;
            font-size: 10px;
            touch-action: manipulation;
        }

        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 3px;
            background: #1a1a1a;
            padding: 2px 5px;
            border-radius: 30px;
            border: 1px solid #4a2a6a;
        }

        #gameScreen {
            padding: 0;
            background: #0a0a0a;
            position: relative;
        }

        .game-container {
            display: flex;
            flex-direction: column;
            height: 100%;
            width: 100%;
        }

        #gameCanvas {
            width: 100%;
            height: 100%;
            background: #0a0a1a;
            border-radius: 10px;
            display: block;
            touch-action: none;
            cursor: pointer;
            flex: 1;
        }

        .game-controls {
            position: absolute;
            bottom: 2px;
            left: 0;
            right: 0;
            padding: 0 2px;
            display: flex;
            justify-content: space-between;
            pointer-events: none;
            z-index: 20;
            flex-wrap: wrap;
        }

        .game-controls button {
            pointer-events: auto;
            background: rgba(0,0,0,0.85);
            border: 2px solid #4a2a6a;
            color: white;
            border-radius: 30px;
            padding: 2px 6px;
            font-size: 7px;
            font-weight: bold;
            backdrop-filter: blur(4px);
            cursor: pointer;
            touch-action: manipulation;
            min-width: 28px;
            min-height: 28px;
        }

        .game-controls button:active {
            transform: scale(0.9);
        }

        .game-controls .pause-btn {
            border-color: #ffdd44;
            background: rgba(255, 200, 50, 0.2);
        }

        .game-controls .quit-btn {
            border-color: #ff4444;
            background: rgba(255, 50, 50, 0.2);
        }

        #pauseOverlay {
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.85);
            z-index: 30;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            border-radius: 10px;
            animation: fadeIn 0.3s ease;
        }

        #pauseOverlay .pause-text {
            font-size: 32px;
            font-weight: bold;
            color: #ffdd44;
            text-shadow: 0 0 30px #ff8800;
            text-align: center;
        }

        #pauseOverlay .pause-sub {
            font-size: 14px;
            color: #88dd88;
            margin-top: 5px;
            text-align: center;
        }

        #pauseOverlay .pause-buttons {
            display: flex;
            gap: 10px;
            margin-top: 15px;
        }

        #pauseOverlay .pause-buttons button {
            padding: 8px 20px;
            border-radius: 30px;
            font-size: 12px;
            font-weight: bold;
            cursor: pointer;
            touch-action: manipulation;
            border: 2px solid #4a2a6a;
            background: #1a1a1a;
            color: white;
        }

        #pauseOverlay .pause-buttons button:active {
            transform: scale(0.95);
        }

        #pauseOverlay .pause-buttons .resume-btn {
            border-color: #44ff88;
            background: rgba(68, 255, 136, 0.2);
        }

        #pauseOverlay .pause-buttons .quit-btn {
            border-color: #ff4444;
            background: rgba(255, 50, 50, 0.2);
        }

        #resultOverlay {
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.9);
            z-index: 35;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            border-radius: 10px;
            animation: fadeIn 0.5s ease;
        }

        #resultOverlay .result-text {
            font-size: 28px;
            font-weight: bold;
            color: #ffdd44;
            text-shadow: 0 0 30px #ff8800;
            text-align: center;
        }

        #resultOverlay .result-stars {
            font-size: 40px;
            margin-top: 5px;
            text-align: center;
        }

        #resultOverlay .result-score {
            font-size: 16px;
            color: #88dd88;
            margin-top: 5px;
            text-align: center;
        }

        #resultOverlay .result-buttons {
            display: flex;
            gap: 10px;
            margin-top: 15px;
        }

        #resultOverlay .result-buttons button {
            padding: 8px 20px;
            border-radius: 30px;
            font-size: 12px;
            font-weight: bold;
            cursor: pointer;
            touch-action: manipulation;
            border: 2px solid #4a2a6a;
            background: #1a1a1a;
            color: white;
        }

        #resultOverlay .result-buttons button:active {
            transform: scale(0.95);
        }

        .music-control {
            background: #1a1a1a;
            border: 1px solid #4a2a6a;
            border-radius: 8px;
            padding: 4px;
            margin: 3px 0;
            display: flex;
            align-items: center;
            gap: 3px;
            flex-wrap: wrap;
        }

        .music-control input[type="file"] {
            display: none;
        }

        .music-control .file-label {
            background: #2a1a3a;
            padding: 2px 6px;
            border-radius: 10px;
            font-size: 7px;
            cursor: pointer;
            border: 1px solid #4a2a6a;
            color: white;
        }

        .music-control .file-label:active {
            background: #3a2a4a;
        }

        .music-control .song-name {
            color: #88dd88;
            font-size: 6px;
            flex: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            max-width: 80px;
        }

        .music-instruction {
            font-size: 6px;
            color: #88dd88;
            padding: 4px;
            background: #0a0a1a;
            border-radius: 6px;
            margin-top: 2px;
            text-align: center;
            border: 1px solid #2a1a3a;
            line-height: 1.5;
        }

        .music-instruction .highlight {
            color: #ff44aa;
        }

        .difficulty-select {
            display: flex;
            gap: 4px;
            margin: 4px 0;
            flex-wrap: wrap;
            justify-content: center;
        }

        .difficulty-select .diff-btn {
            padding: 4px 12px;
            border-radius: 30px;
            font-size: 8px;
            font-weight: bold;
            cursor: pointer;
            border: 2px solid #4a2a6a;
            background: #1a1a1a;
            color: white;
            touch-action: manipulation;
        }

        .difficulty-select .diff-btn.active {
            border-color: #ff44aa;
            background: #2a1a3a;
        }

        @media (max-width: 420px) {
            #app {
                border-radius: 0;
                height: 100vh;
                max-height: 100vh;
                padding: 2px;
            }
            .menu-btn {
                padding: 4px 10px;
                font-size: 10px;
            }
            .game-controls button {
                padding: 1px 4px;
                font-size: 6px;
                min-width: 22px;
                min-height: 22px;
            }
            #loadingScreen .logo {
                font-size: 40px;
            }
            #loadingScreen .title {
                font-size: 24px;
            }
            #resultOverlay .result-text {
                font-size: 22px;
            }
            #resultOverlay .result-stars {
                font-size: 30px;
            }
            #pauseOverlay .pause-text {
                font-size: 24px;
            }
        }

        @media (max-height: 600px) {
            .game-controls button {
                min-width: 20px;
                min-height: 20px;
                font-size: 5px;
                padding: 1px 3px;
            }
            #pauseOverlay .pause-text {
                font-size: 20px;
            }
            #pauseOverlay .pause-buttons button {
                padding: 4px 12px;
                font-size: 10px;
            }
        }
    </style>
</head>
<body>

<div id="app">
    <!-- LOADING SCREEN -->
    <div id="loadingScreen">
        <div class="logo">🎹</div>
        <div class="title">PIANO FIRE</div>
        <div class="sub">TAP THE TILES</div>
        <div class="load-bar">
            <div class="load-fill" id="loadFill"></div>
        </div>
        <div class="load-percent" id="loadPercent">0%</div>
        <div class="tap-text">👆 TAP TO START</div>
    </div>

    <!-- HOME SCREEN -->
    <div id="homeScreen" class="screen">
        <div class="logo">🎹 PIANO FIRE</div>
        <div class="sub">TAP THE TILES</div>
        <div style="display:flex; gap:2px; margin-bottom:4px; flex-wrap:wrap; justify-content:center;">
            <span class="coin-display" style="border-color:#ff44aa;">🎵 PLAY</span>
        </div>
        <button class="menu-btn pink" onclick="showScreen('songSelectScreen')">▶ SELECT SONG</button>
        <button class="menu-btn" onclick="startGame()">⚡ QUICK PLAY</button>
        <button class="menu-btn small-btn" onclick="showScreen('settingsScreen')">⚙️ SETTINGS</button>
        <div style="margin-top:6px; font-size:6px; color:#557755;">"TAP THE TILES!"</div>
    </div>

    <!-- SONG SELECT -->
    <div id="songSelectScreen" class="screen">
        <div class="header">
            <button class="back-btn" onclick="showScreen('homeScreen')">←</button>
            <span style="font-size:10px;">🎵 SELECT SONG</span>
            <span style="font-size:8px; color:#88dd88;" id="songStatus">No song loaded</span>
        </div>
        
        <div class="music-control">
            <span style="font-size:7px; color:#88dd88;">🎵 MUSIC:</span>
            <label class="file-label" for="musicFile">📁 UPLOAD MP3</label>
            <input type="file" id="musicFile" accept=".mp3,.wav,.ogg" onchange="uploadMusic(event)" />
            <span class="song-name" id="songName">No song selected</span>
        </div>
        
        <div class="music-instruction">
            📌 <span class="highlight">HOW TO ADD MUSIC:</span><br>
            1️⃣ Download MP3 from <span class="highlight">audio.com</span> or any MP3 site<br>
            2️⃣ Click <span class="highlight">"UPLOAD MP3"</span> and select your file<br>
            3️⃣ Choose difficulty and play!
        </div>

        <div style="font-size:8px; color:#88dd88; text-align:center; margin-top:4px;">🎯 DIFFICULTY</div>
        <div class="difficulty-select">
            <button class="diff-btn" onclick="setDifficulty('easy')" id="diffEasy">🟢 EASY</button>
            <button class="diff-btn active" onclick="setDifficulty('medium')" id="diffMedium">🟡 MEDIUM</button>
            <button class="diff-btn" onclick="setDifficulty('hard')" id="diffHard">🔴 HARD</button>
            <button class="diff-btn" onclick="setDifficulty('expert')" id="diffExpert">🔵 EXPERT</button>
        </div>

        <button class="menu-btn pink" style="margin-top:4px;" onclick="startGame()">▶ START GAME</button>
        <div style="font-size:6px; color:#557755; text-align:center; margin-top:2px;">
            🎵 Tiles change color at the extreme part of the song!
        </div>
    </div>

    <!-- GAME SCREEN -->
    <div id="gameScreen" class="screen">
        <div class="game-container">
            <canvas id="gameCanvas"></canvas>
            <div id="pauseOverlay">
                <div class="pause-text">⏸️ PAUSED</div>
                <div class="pause-sub">Game is paused</div>
                <div class="pause-buttons">
                    <button class="resume-btn" onclick="togglePause()">▶ RESUME</button>
                    <button class="quit-btn" onclick="quitGame()">🚪 QUIT</button>
                </div>
            </div>
            <div id="resultOverlay">
                <div class="result-text" id="resultText">🎉 GREAT!</div>
                <div class="result-stars" id="resultStars">⭐⭐⭐</div>
                <div class="result-score" id="resultScore">Score: 0</div>
                <div class="result-buttons">
                    <button onclick="closeResult()">🎮 MENU</button>
                    <button onclick="restartGame()">🔄 RETRY</button>
                </div>
            </div>
            <div class="game-controls">
                <div class="btn-row">
                    <button class="quit-btn" onclick="quitGame()">🚪</button>
                    <button class="pause-btn" onclick="togglePause()">⏸️</button>
                </div>
                <div class="btn-row">
                    <span style="color:#ffdd44; font-size:8px; padding:4px;" id="scoreDisplay">⭐ 0</span>
                    <span style="color:#ff44aa; font-size:8px; padding:4px;" id="comboDisplay">🔥 0</span>
                </div>
            </div>
        </div>
    </div>

    <!-- SETTINGS -->
    <div id="settingsScreen" class="screen">
        <div class="header">
            <button class="back-btn" onclick="showScreen('homeScreen')">←</button>
            <span style="font-size:10px;">⚙️ SETTINGS</span>
            <span></span>
        </div>
        <div style="margin-top:8px; font-size:10px;">
            <div>🎵 Sound: <span class="text-gold" id="soundSetting">ON</span></div>
            <div>🎮 Controls: <span class="text-gold">Touch Anywhere</span></div>
            <div>🎯 Difficulty: <span class="text-gold" id="diffSetting">Medium</span></div>
            <div>⭐ High Score: <span class="text-gold" id="highScoreDisplay">0</span></div>
            <button class="menu-btn small-btn" style="margin-top:4px;" onclick="toggleSound()">🔊 TOGGLE SOUND</button>
            <button class="menu-btn small-btn" style="margin-top:2px;" onclick="showComingSoon('More Songs')">🎵 MORE SONGS</button>
        </div>
        <div style="margin-top:8px; font-size:6px; color:#557755; text-align:center;">
            💡 Download songs from <span style="color:#ff44aa;">audio.com</span> or any MP3 site
        </div>
    </div>
</div>

<script>
    // ========== LOADING SCREEN ==========
    let loadingProgress = 0;
    const loadFill = document.getElementById('loadFill');
    const loadPercent = document.getElementById('loadPercent');
    const loadingScreen = document.getElementById('loadingScreen');

    const loadInterval = setInterval(() => {
        loadingProgress += Math.random() * 8 + 2;
        if (loadingProgress > 100) loadingProgress = 100;
        loadFill.style.width = loadingProgress + '%';
        loadPercent.textContent = Math.floor(loadingProgress) + '%';
        if (loadingProgress >= 100) {
            clearInterval(loadInterval);
            document.querySelector('.tap-text').style.display = 'block';
        }
    }, 100);

    loadingScreen.addEventListener('click', function() {
        if (loadingProgress >= 100) {
            this.style.display = 'none';
            document.getElementById('homeScreen').classList.add('active');
            updateHighScore();
        }
    });

    // ========== GAME STATE ==========
    let gameActive = false;
    let gamePaused = false;
    let score = 0;
    let combo = 0;
    let highScore = 0;
    let tiles = [];
    let tileSpeed = 2;
    let tileWidth = 80;
    let tileHeight = 80;
    let nextTileY = 0;
    let tileGap = 30;
    let gameTime = 0;
    let difficulty = 'medium';
    let audioElement = null;
    let musicLoaded = false;
    let soundEnabled = true;
    let gameLoop = null;
    let frameId = null;
    let colorChangeTimer = 0;
    let isExtremeMode = false;
    let totalTiles = 0;
    let hitTiles = 0;
    let missedTiles = 0;
    let stars = 0;
    let canvas = null;
    let ctx = null;
    let W = 400;
    let H = 700;
    let spawnTimer = 0;
    let spawnInterval = 30;
    let maxMissed = 15;
    let extremeTimer = 0;
    let extremeDuration = 0;
    let extremeActive = false;
    let tileSpawnCount = 0;
    let lastTileLane = -1;

    // Colors
    const colors = {
        normal: '#ff44aa',
        normalDark: '#cc2288',
        normalLight: '#ff66cc',
        extreme: '#ff00ff',
        extremeDark: '#cc00cc',
        extremeLight: '#ff44ff',
        background: '#0a0a1a',
        laneBg: 'rgba(255,255,255,0.05)',
        hit: '#44ff88',
        miss: '#ff4444',
        combo: '#ffdd44'
    };

    // ========== MUSIC SYSTEM ==========
    function uploadMusic(event) {
        const file = event.target.files[0];
        if (file) {
            const url = URL.createObjectURL(file);
            if (audioElement) {
                audioElement.src = url;
                audioElement.load();
            } else {
                audioElement = new Audio(url);
            }
            musicLoaded = true;
            document.getElementById('songName').textContent = file.name;
            document.getElementById('songStatus').textContent = `✅ ${file.name}`;
            document.getElementById('songStatus').style.color = '#88dd88';
            
            try {
                const reader = new FileReader();
                reader.onload = function(e) {
                    localStorage.setItem('pianoFireMusic', e.target.result);
                    localStorage.setItem('pianoFireMusicName', file.name);
                };
                reader.readAsDataURL(file);
            } catch(e) {}
        }
    }

    function loadStoredMusic() {
        try {
            const stored = localStorage.getItem('pianoFireMusic');
            const name = localStorage.getItem('pianoFireMusicName');
            if (stored && name) {
                if (audioElement) {
                    audioElement.src = stored;
                    audioElement.load();
                } else {
                    audioElement = new Audio(stored);
                }
                musicLoaded = true;
                document.getElementById('songName').textContent = name;
                document.getElementById('songStatus').textContent = `✅ ${name}`;
                document.getElementById('songStatus').style.color = '#88dd88';
            }
        } catch(e) {}
    }

    function playMusic() {
        if (audioElement && musicLoaded) {
            try {
                audioElement.currentTime = 0;
                audioElement.play();
            } catch(e) {}
        }
    }

    function stopMusic() {
        if (audioElement) {
            try {
                audioElement.pause();
            } catch(e) {}
        }
    }

    function pauseMusic() {
        if (audioElement && !audioElement.paused) {
            try {
                audioElement.pause();
            } catch(e) {}
        }
    }

    function resumeMusic() {
        if (audioElement && audioElement.paused && gameActive && !gamePaused) {
            try {
                audioElement.play();
            } catch(e) {}
        }
    }

    // ========== DIFFICULTY ==========
    function setDifficulty(diff) {
        difficulty = diff;
        document.querySelectorAll('.diff-btn').forEach(b => b.classList.remove('active'));
        const map = { easy: 'diffEasy', medium: 'diffMedium', hard: 'diffHard', expert: 'diffExpert' };
        const el = document.getElementById(map[diff]);
        if (el) el.classList.add('active');
        document.getElementById('diffSetting').textContent = diff.charAt(0).toUpperCase() + diff.slice(1);
        
        const speedMap = { easy: 0.8, medium: 1.5, hard: 2.5, expert: 4 };
        tileSpeed = speedMap[diff] || 1.5;
        const spawnMap = { easy: 45, medium: 35, hard: 25, expert: 18 };
        spawnInterval = spawnMap[diff] || 35;
        maxMissed = { easy: 25, medium: 20, hard: 15, expert: 10 }[diff] || 20;
        tileGap = { easy: 40, medium: 30, hard: 25, expert: 20 }[diff] || 30;
    }

    // ========== HIGH SCORE ==========
    function updateHighScore() {
        const saved = localStorage.getItem('pianoFireHighScore');
        if (saved) {
            highScore = parseInt(saved) || 0;
        }
        document.getElementById('highScoreDisplay').textContent = highScore;
    }

    function saveHighScore() {
        if (score > highScore) {
            highScore = score;
            localStorage.setItem('pianoFireHighScore', String(highScore));
            document.getElementById('highScoreDisplay').textContent = highScore;
        }
    }

    // ========== START GAME ==========
    function startGame() {
        showScreen('gameScreen');
        initGameCanvas();
        resetGame();
        startGameLoop();
        if (musicLoaded) {
            playMusic();
        }
    }

    function resetGame() {
        score = 0;
        combo = 0;
        tiles = [];
        nextTileY = 0;
        gameTime = 0;
        colorChangeTimer = 0;
        isExtremeMode = false;
        extremeTimer = 0;
        extremeActive = false;
        spawnTimer = 0;
        tileSpawnCount = 0;
        totalTiles = 0;
        hitTiles = 0;
        missedTiles = 0;
        stars = 0;
        gameActive = true;
        gamePaused = false;
        lastTileLane = -1;
        document.getElementById('pauseOverlay').style.display = 'none';
        document.getElementById('resultOverlay').style.display = 'none';
        document.getElementById('scoreDisplay').textContent = '⭐ 0';
        document.getElementById('comboDisplay').textContent = '🔥 0';
        
        // Generate initial tiles
        generateTiles(10);
    }

    function generateTiles(count) {
        for (let i = 0; i < count; i++) {
            // Avoid same lane consecutively
            let lane;
            do {
                lane = Math.floor(Math.random() * 4);
            } while (lane === lastTileLane && tiles.length > 0);
            lastTileLane = lane;
            
            const y = nextTileY;
            nextTileY += tileHeight + tileGap;
            
            // Determine if this tile should be in extreme mode
            const useExtreme = isExtremeMode || (extremeActive && Math.random() < 0.6);
            
            tiles.push({
                lane: lane,
                x: lane * (tileWidth + 5) + 10,
                y: y,
                width: tileWidth,
                height: tileHeight,
                hit: false,
                missed: false,
                color: useExtreme ? colors.extreme : colors.normal,
                darkColor: useExtreme ? colors.extremeDark : colors.normalDark,
                isExtreme: useExtreme
            });
            totalTiles++;
            tileSpawnCount++;
        }
    }

    function initGameCanvas() {
        canvas = document.getElementById('gameCanvas');
        if (canvas) {
            W = canvas.width = canvas.clientWidth || 400;
            H = canvas.height = canvas.clientHeight || 700;
            tileWidth = (W - 20) / 4 - 5;
            tileHeight = tileWidth * 0.9;
            ctx = canvas.getContext('2d');
        }
    }

    // ========== GAME LOOP ==========
    function startGameLoop() {
        if (gameLoop) clearInterval(gameLoop);
        if (frameId) cancelAnimationFrame(frameId);

        gameLoop = setInterval(() => {
            if (!gameActive || gamePaused) return;
            
            gameTime++;
            colorChangeTimer++;
            spawnTimer++;
            extremeTimer++;
            
            // EXTREME MODE - triggers periodically and lasts for a duration
            if (extremeTimer > 200) {
                extremeTimer = 0;
                extremeActive = true;
                isExtremeMode = true;
                // Update existing tiles to extreme colors
                tiles.forEach(t => {
                    if (!t.hit && !t.missed) {
                        t.color = colors.extreme;
                        t.darkColor = colors.extremeDark;
                        t.isExtreme = true;
                    }
                });
                // Extreme mode lasts for 60-100 frames
                extremeDuration = 60 + Math.floor(Math.random() * 40);
            }
            
            // End extreme mode
            if (extremeActive && extremeTimer > extremeDuration) {
                extremeActive = false;
                isExtremeMode = false;
                // Revert tiles to normal colors
                tiles.forEach(t => {
                    if (!t.hit && !t.missed) {
                        t.color = colors.normal;
                        t.darkColor = colors.normalDark;
                        t.isExtreme = false;
                    }
                });
            }

            // Move tiles down
            tiles.forEach(t => {
                if (!t.hit && !t.missed) {
                    t.y += tileSpeed;
                    // Check if tile passed bottom
                    if (t.y > H + 50) {
                        t.missed = true;
                        missedTiles++;
                        combo = 0;
                        document.getElementById('comboDisplay').textContent = '🔥 0';
                        // Check game over
                        if (missedTiles >= maxMissed) {
                            gameActive = false;
                            clearInterval(gameLoop);
                            gameOver();
                        }
                    }
                }
            });

            // Remove missed tiles that are far gone
            tiles = tiles.filter(t => !t.missed || t.y < H + 100);
            
            // Spawn new tiles
            if (spawnTimer >= spawnInterval) {
                spawnTimer = 0;
                const lastTile = tiles[tiles.length - 1];
                if (!lastTile || lastTile.y > 50) {
                    // Spawn more tiles during extreme mode
                    const count = isExtremeMode ? 5 : 3;
                    generateTiles(count);
                }
            }

            // Check if no tiles left and game should end
            if (tiles.length === 0 && gameActive && totalTiles > 20) {
                gameActive = false;
                clearInterval(gameLoop);
                gameOver();
            }

            drawGame();
        }, 50);

        function animate() {
            if (!gameActive) return;
            drawGame();
            frameId = requestAnimationFrame(animate);
        }
        animate();
    }

    // ========== DRAW GAME ==========
    function drawGame() {
        if (!canvas || !ctx) return;
        
        ctx.clearRect(0, 0, W, H);

        // Background
        ctx.fillStyle = colors.background;
        ctx.fillRect(0, 0, W, H);

        // Lane dividers
        const laneWidth = (W - 10) / 4;
        for (let i = 0; i < 4; i++) {
            ctx.fillStyle = colors.laneBg;
            ctx.fillRect(5 + i * laneWidth, 0, laneWidth - 2, H);
        }

        // Draw tiles
        tiles.forEach(t => {
            if (t.hit) return;
            
            const color = t.color || colors.normal;
            const darkColor = t.darkColor || colors.normalDark;
            
            // Glow effect for extreme tiles
            if (t.isExtreme || isExtremeMode) {
                ctx.shadowColor = color;
                ctx.shadowBlur = 25;
            }
            
            const radius = 10;
            const x = t.x;
            const y = t.y;
            const w = t.width || tileWidth;
            const h = t.height || tileHeight;
            
            ctx.beginPath();
            ctx.moveTo(x + radius, y);
            ctx.lineTo(x + w - radius, y);
            ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
            ctx.lineTo(x + w, y + h - radius);
            ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
            ctx.lineTo(x + radius, y + h);
            ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
            ctx.lineTo(x, y + radius);
            ctx.quadraticCurveTo(x, y, x + radius, y);
            ctx.closePath();
            
            // Gradient
            const gradient = ctx.createLinearGradient(x, y, x, y + h);
            gradient.addColorStop(0, color);
            gradient.addColorStop(0.6, color);
            gradient.addColorStop(1, darkColor);
            ctx.fillStyle = gradient;
            ctx.fill();
            
            // Shine effect
            ctx.shadowBlur = 0;
            ctx.fillStyle = 'rgba(255,255,255,0.1)';
            ctx.fillRect(x + 8, y + 8, w - 16, h * 0.25);
            
            // Border
            ctx.strokeStyle = 'rgba(255,255,255,0.1)';
            ctx.lineWidth = 1;
            ctx.stroke();
        });

        // Bottom line (hit zone)
        const lineY = H - 70;
        ctx.strokeStyle = 'rgba(255,68,170,0.4)';
        ctx.lineWidth = 2;
        ctx.setLineDash([8, 8]);
        ctx.beginPath();
        ctx.moveTo(10, lineY);
        ctx.lineTo(W - 10, lineY);
        ctx.stroke();
        ctx.setLineDash([]);

        // Tap zone (full width)
        ctx.fillStyle = 'rgba(255,68,170,0.06)';
        ctx.fillRect(10, lineY + 2, W - 20, 40);
        
        // Tap zone text
        ctx.fillStyle = 'rgba(255,68,170,0.25)';
        ctx.font = '8px sans-serif';
        ctx.textAlign = 'center';
        ctx.fillText('▼ TAP ANYWHERE TO HIT ▼', W/2, lineY + 25);
        ctx.textAlign = 'left';

        // Extreme mode indicator
        if (isExtremeMode || extremeActive) {
            ctx.fillStyle = 'rgba(255,0,255,0.08)';
            ctx.fillRect(0, 0, W, H);
            ctx.fillStyle = '#ff00ff';
            ctx.font = 'bold 16px sans-serif';
            ctx.textAlign = 'center';
            ctx.fillText('⚡ EXTREME MODE ⚡', W/2, 30);
            ctx.textAlign = 'left';
            
            // Glow pulse
            ctx.shadowColor = '#ff00ff';
            ctx.shadowBlur = 50;
            ctx.fillStyle = 'rgba(255,0,255,0.02)';
            ctx.fillRect(0, 0, W, H);
            ctx.shadowBlur = 0;
        }

        // Difficulty indicator
        const diffColors = { easy: '#44ff44', medium: '#ffdd44', hard: '#ff4444', expert: '#4444ff' };
        ctx.fillStyle = diffColors[difficulty] || '#ffffff';
        ctx.font = '8px sans-serif';
        ctx.fillText(`🎯 ${difficulty.toUpperCase()}`, 10, 15);

        // Missed count
        ctx.fillStyle = 'rgba(255,68,68,0.6)';
        ctx.font = '8px sans-serif';
        ctx.textAlign = 'right';
        ctx.fillText(`❌ ${missedTiles}/${maxMissed}`, W - 10, 15);
        ctx.textAlign = 'left';

        // Tile count
        const activeTiles = tiles.filter(t => !t.hit && !t.missed).length;
        ctx.fillStyle = 'rgba(255,255,255,0.2)';
        ctx.font = '7px sans-serif';
        ctx.textAlign = 'right';
        ctx.fillText(`🎵 ${activeTiles}`, W - 10, 28);
        ctx.textAlign = 'left';
    }

    // ========== TAP HANDLING - ANYWHERE ON SCREEN ==========
    function handleTap(x, y) {
        if (!gameActive || gamePaused) return;
        if (!canvas) return;
        
        // Get canvas coordinates
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        const canvasX = (x - rect.left) * scaleX;
        const canvasY = (y - rect.top) * scaleY;
        
        // Ignore taps at the very top (UI area)
        if (canvasY < 20) return;
        
        // Find the closest tile to the tap position
        let closestTile = null;
        let closestDist = Infinity;
        const lineY = H - 70;
        
        tiles.forEach(t => {
            if (t.hit || t.missed) return;
            // Check if tap is roughly above the tile
            const tileCenterY = t.y + t.height / 2;
            const distY = Math.abs(tileCenterY - (lineY + 20));
            const distX = Math.abs((t.x + t.width/2) - canvasX);
            
            // Only consider tiles near the bottom zone
            if (distY < tileHeight + 40 && distX < tileWidth) {
                const totalDist = Math.sqrt(distX * distX + distY * distY);
                if (totalDist < closestDist) {
                    closestDist = totalDist;
                    closestTile = t;
                }
            }
        });

        // Also check any tile regardless of position (for extreme mode)
        if (!closestTile) {
            tiles.forEach(t => {
                if (t.hit || t.missed) return;
                const tileCenterY = t.y + t.height / 2;
                const distY = Math.abs(tileCenterY - (lineY + 20));
                const distX = Math.abs((t.x + t.width/2) - canvasX);
                
                if (distY < tileHeight * 1.5 && distX < tileWidth * 1.2) {
                    const totalDist = Math.sqrt(distX * distX + distY * distY);
                    if (totalDist < closestDist) {
                        closestDist = totalDist;
                        closestTile = t;
                    }
                }
            });
        }

        if (closestTile) {
            // Hit!
            closestTile.hit = true;
            hitTiles++;
            combo++;
            const comboBonus = Math.min(combo, 5);
            score += 10 * comboBonus;
            
            // Update displays
            document.getElementById('scoreDisplay').textContent = `⭐ ${score}`;
            document.getElementById('comboDisplay').textContent = `🔥 ${combo}`;
            
            // Sound effect
            if (soundEnabled) {
                try {
                    const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                    const oscillator = audioCtx.createOscillator();
                    const gainNode = audioCtx.createGain();
                    oscillator.connect(gainNode);
                    gainNode.connect(audioCtx.destination);
                    oscillator.frequency.value = 440 + (closestTile.lane * 100);
                    oscillator.type = 'sine';
                    gainNode.gain.value = 0.08;
                    oscillator.start();
                    oscillator.stop(audioCtx.currentTime + 0.08);
                } catch(e) {}
            }

            // Remove tile after delay
            setTimeout(() => {
                tiles = tiles.filter(t => t !== closestTile);
            }, 150);
        } else {
            // Miss - but only if tap is in the bottom area
            if (canvasY > H - 100) {
                combo = 0;
                document.getElementById('comboDisplay').textContent = '🔥 0';
                if (soundEnabled) {
                    try {
                        const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                        const oscillator = audioCtx.createOscillator();
                        const gainNode = audioCtx.createGain();
                        oscillator.connect(gainNode);
                        gainNode.connect(audioCtx.destination);
                        oscillator.frequency.value = 200;
                        oscillator.type = 'sawtooth';
                        gainNode.gain.value = 0.04;
                        oscillator.start();
                        oscillator.stop(audioCtx.currentTime + 0.1);
                    } catch(e) {}
                }
            }
        }
    }

    // ========== CANVAS TOUCH EVENTS ==========
    function setupCanvasTouch() {
        const canvas = document.getElementById('gameCanvas');
        if (!canvas) return;

        canvas.addEventListener('touchstart', (e) => {
            e.preventDefault();
            const touch = e.touches[0];
            if (touch) handleTap(touch.clientX, touch.clientY);
        }, { passive: false });

        canvas.addEventListener('mousedown', (e) => {
            handleTap(e.clientX, e.clientY);
        });
    }

    // ========== PAUSE / QUIT ==========
    function togglePause() {
        if (!gameActive) return;
        gamePaused = !gamePaused;
        document.getElementById('pauseOverlay').style.display = gamePaused ? 'flex' : 'none';
        if (gamePaused) {
            pauseMusic();
        } else {
            resumeMusic();
        }
    }

    function quitGame() {
        gameActive = false;
        if (gameLoop) clearInterval(gameLoop);
        if (frameId) cancelAnimationFrame(frameId);
        stopMusic();
        document.getElementById('pauseOverlay').style.display = 'none';
        document.getElementById('resultOverlay').style.display = 'none';
        showScreen('homeScreen');
    }

    function restartGame() {
        document.getElementById('resultOverlay').style.display = 'none';
        resetGame();
        if (musicLoaded) {
            playMusic();
        }
    }

    function closeResult() {
        document.getElementById('resultOverlay').style.display = 'none';
        gameActive = false;
        stopMusic();
        showScreen('songSelectScreen');
    }

    // ========== GAME OVER ==========
    function gameOver() {
        if (!gameActive) return;
        gameActive = false;
        if (gameLoop) clearInterval(gameLoop);
        if (frameId) cancelAnimationFrame(frameId);
        stopMusic();
        
        // Calculate stars
        const accuracy = totalTiles > 0 ? hitTiles / totalTiles : 0;
        if (accuracy > 0.85) stars = 3;
        else if (accuracy > 0.65) stars = 2;
        else if (accuracy > 0.45) stars = 1;
        else stars = 0;
        
        // Save high score
        saveHighScore();
        
        // Show result
        const resultText = document.getElementById('resultText');
        const resultStars = document.getElementById('resultStars');
        const resultScore = document.getElementById('resultScore');
        
        if (accuracy > 0.85) resultText.textContent = '🎉 PERFECT!';
        else if (accuracy > 0.65) resultText.textContent = '🌟 GREAT!';
        else if (accuracy > 0.45) resultText.textContent = '👍 GOOD!';
        else resultText.textContent = '💪 KEEP PRACTICING!';
        
        resultStars.textContent = '⭐'.repeat(stars) + '☆'.repeat(3 - stars);
        resultScore.textContent = `Score: ${score} | Hit: ${hitTiles}/${totalTiles} | Combo: ${combo}`;
        
        document.getElementById('resultOverlay').style.display = 'flex';
    }

    // ========== UI HELPERS ==========
    function showScreen(id) {
        document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
        const target = document.getElementById(id);
        if (target) target.classList.add('active');
        if (id === 'gameScreen') {
            setTimeout(() => {
                initGameCanvas();
                setupCanvasTouch();
            }, 100);
        }
        if (id === 'settingsScreen') {
            document.getElementById('soundSetting').textContent = soundEnabled ? 'ON' : 'OFF';
            updateHighScore();
        }
        if (id === 'songSelectScreen') {
            loadStoredMusic();
        }
    }

    function toggleSound() {
        soundEnabled = !soundEnabled;
        document.getElementById('soundSetting').textContent = soundEnabled ? 'ON' : 'OFF';
    }

    function showComingSoon(feature) {
        alert(`🔜 ${feature} coming soon!\nStay tuned for updates!`);
    }

    // ========== INIT ==========
    setDifficulty('medium');
    loadStoredMusic();
    updateHighScore();

    // Handle resize
    function handleResize() {
        const canvas = document.getElementById('gameCanvas');
        if (canvas) {
            W = canvas.width = canvas.clientWidth || 400;
            H = canvas.height = canvas.clientHeight || 700;
            tileWidth = (W - 20) / 4 - 5;
            tileHeight = tileWidth * 0.9;
            ctx = canvas.getContext('2d');
        }
    }

    window.addEventListener('resize', handleResize);
    window.addEventListener('orientationchange', () => {
        setTimeout(handleResize, 400);
    });

    document.addEventListener('DOMContentLoaded', () => {
        setTimeout(handleResize, 300);
    });

    // Keyboard shortcuts
    document.addEventListener('keydown', (e) => {
        if (e.key === ' ' || e.key === 'p') {
            if (document.getElementById('gameScreen').classList.contains('active')) {
                togglePause();
            }
        }
        if (e.key === 'q' || e.key === 'Q') {
            if (document.getElementById('gameScreen').classList.contains('active')) {
                quitGame();
            }
        }
        // Tap with keyboard for testing (A key)
        if (e.key === 'a' || e.key === 'A') {
            if (document.getElementById('gameScreen').classList.contains('active') && gameActive) {
                handleTap(200, 600);
            }
        }
    });

    console.log('🎹 Piano Fire - Tap the tiles!');
    console.log('📌 Download music from audio.com or any MP3 site');
    console.log('🎯 Difficulty: Easy, Medium, Hard, Expert');
    console.log('⭐ Get 3 stars for perfect performance!');
    console.log('⏸️ Press Space/P to pause, Q to quit');
    console.log('👆 Tap anywhere on the screen to hit tiles!');
</script>
</body>
</html>

Game Source: Piano Fire

Creator: SonicBear35

Libraries: none

Complexity: complex (1500 lines, 48.1 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: piano-fire-sonicbear35" to link back to the original. Then publish at arcadelab.ai/publish.