Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Bubble Pop Frenzy - Touch Screen Game</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| touch-action: manipulation; | |
| user-select: none; | |
| } | |
| body { | |
| font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif; | |
| background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); | |
| color: white; | |
| overflow: hidden; | |
| height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .container { | |
| width: 100%; | |
| max-width: 800px; | |
| height: 90vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 15px; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 15px; | |
| width: 100%; | |
| } | |
| h1 { | |
| font-size: 2.8rem; | |
| margin-bottom: 5px; | |
| background: linear-gradient(90deg, #ff9a9e, #fad0c4, #fad0c4, #a18cd1); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| text-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); | |
| } | |
| .subtitle { | |
| font-size: 1.2rem; | |
| color: #a1c4fd; | |
| margin-bottom: 10px; | |
| } | |
| .game-info { | |
| display: flex; | |
| justify-content: space-between; | |
| width: 100%; | |
| background: rgba(0, 0, 0, 0.3); | |
| border-radius: 15px; | |
| padding: 15px; | |
| margin-bottom: 15px; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); | |
| border: 2px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .info-box { | |
| text-align: center; | |
| flex: 1; | |
| } | |
| .info-title { | |
| font-size: 0.9rem; | |
| color: #a1c4fd; | |
| margin-bottom: 5px; | |
| } | |
| .info-value { | |
| font-size: 2.2rem; | |
| font-weight: bold; | |
| color: #ffd166; | |
| text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); | |
| } | |
| .game-container { | |
| position: relative; | |
| width: 100%; | |
| flex-grow: 1; | |
| background: rgba(10, 15, 35, 0.7); | |
| border-radius: 20px; | |
| overflow: hidden; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4); | |
| border: 3px solid rgba(255, 255, 255, 0.1); | |
| margin-bottom: 15px; | |
| } | |
| #game-canvas { | |
| width: 100%; | |
| height: 100%; | |
| display: block; | |
| } | |
| .controls { | |
| display: flex; | |
| justify-content: center; | |
| gap: 20px; | |
| width: 100%; | |
| margin-top: 10px; | |
| } | |
| .btn { | |
| padding: 15px 30px; | |
| font-size: 1.2rem; | |
| border: none; | |
| border-radius: 50px; | |
| background: linear-gradient(90deg, #ff9a9e, #fad0c4); | |
| color: #333; | |
| font-weight: bold; | |
| cursor: pointer; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); | |
| transition: all 0.2s; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| } | |
| .btn:active { | |
| transform: scale(0.95); | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); | |
| } | |
| .btn-restart { | |
| background: linear-gradient(90deg, #a18cd1, #fbc2eb); | |
| } | |
| .btn-pause { | |
| background: linear-gradient(90deg, #ffd166, #ffef9f); | |
| } | |
| .instructions { | |
| background: rgba(0, 0, 0, 0.3); | |
| border-radius: 15px; | |
| padding: 15px; | |
| margin-top: 15px; | |
| width: 100%; | |
| text-align: center; | |
| font-size: 1rem; | |
| line-height: 1.5; | |
| color: #a1c4fd; | |
| border: 2px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .instructions strong { | |
| color: #ffd166; | |
| } | |
| .game-over { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.85); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 10; | |
| border-radius: 20px; | |
| display: none; | |
| } | |
| .game-over h2 { | |
| font-size: 3.5rem; | |
| color: #ff9a9e; | |
| margin-bottom: 20px; | |
| text-shadow: 0 3px 6px rgba(0, 0, 0, 0.5); | |
| } | |
| .final-score { | |
| font-size: 2.5rem; | |
| color: #ffd166; | |
| margin-bottom: 30px; | |
| } | |
| .bubble-info { | |
| display: flex; | |
| justify-content: center; | |
| gap: 15px; | |
| margin-top: 15px; | |
| flex-wrap: wrap; | |
| } | |
| .bubble-type { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-size: 0.9rem; | |
| } | |
| .bubble-demo { | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 50%; | |
| } | |
| .pulse { | |
| animation: pulse 1.5s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| 100% { transform: scale(1); } | |
| } | |
| @media (max-width: 768px) { | |
| h1 { | |
| font-size: 2.2rem; | |
| } | |
| .info-value { | |
| font-size: 1.8rem; | |
| } | |
| .btn { | |
| padding: 12px 20px; | |
| font-size: 1.1rem; | |
| } | |
| .controls { | |
| gap: 10px; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .container { | |
| padding: 10px; | |
| } | |
| h1 { | |
| font-size: 1.8rem; | |
| } | |
| .subtitle { | |
| font-size: 1rem; | |
| } | |
| .game-info { | |
| padding: 10px; | |
| } | |
| .info-value { | |
| font-size: 1.5rem; | |
| } | |
| .btn { | |
| padding: 10px 15px; | |
| font-size: 1rem; | |
| } | |
| .instructions { | |
| font-size: 0.9rem; | |
| padding: 10px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1><i class="fas fa-bubbles"></i> Bubble Pop Frenzy</h1> | |
| <p class="subtitle">Touch the bubbles to pop them! Don't let them escape!</p> | |
| </div> | |
| <div class="game-info"> | |
| <div class="info-box"> | |
| <div class="info-title">SCORE</div> | |
| <div id="score" class="info-value">0</div> | |
| </div> | |
| <div class="info-box"> | |
| <div class="info-title">TIME LEFT</div> | |
| <div id="timer" class="info-value">60</div> | |
| </div> | |
| <div class="info-box"> | |
| <div class="info-title">BUBBLES</div> | |
| <div id="bubbles-count" class="info-value">0</div> | |
| </div> | |
| </div> | |
| <div class="game-container"> | |
| <canvas id="game-canvas"></canvas> | |
| <div id="game-over" class="game-over"> | |
| <h2>GAME OVER</h2> | |
| <div class="final-score">Score: <span id="final-score">0</span></div> | |
| <button id="restart-btn" class="btn btn-restart pulse"> | |
| <i class="fas fa-redo"></i> PLAY AGAIN | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bubble-info"> | |
| <div class="bubble-type"> | |
| <div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #ff9a9e, #ff6b6b);"></div> | |
| <span>Normal (+10)</span> | |
| </div> | |
| <div class="bubble-type"> | |
| <div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #a18cd1, #6a5acd);"></div> | |
| <span>Speed (+20)</span> | |
| </div> | |
| <div class="bubble-type"> | |
| <div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #ffd166, #ff9e00);"></div> | |
| <span>Bonus (+50)</span> | |
| </div> | |
| <div class="bubble-type"> | |
| <div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #06d6a0, #04a777);"></div> | |
| <span>Time (+5s)</span> | |
| </div> | |
| </div> | |
| <div class="controls"> | |
| <button id="pause-btn" class="btn btn-pause"> | |
| <i class="fas fa-pause"></i> PAUSE | |
| </button> | |
| <button id="start-btn" class="btn"> | |
| <i class="fas fa-play"></i> START | |
| </button> | |
| </div> | |
| <div class="instructions"> | |
| <strong>HOW TO PLAY:</strong> Tap bubbles to pop them. Each bubble type gives different points. Avoid letting bubbles escape off the screen! | |
| </div> | |
| </div> | |
| <script> | |
| // Game variables | |
| let canvas, ctx; | |
| let score = 0; | |
| let timeLeft = 60; | |
| let gameActive = false; | |
| let gamePaused = false; | |
| let animationId; | |
| let bubbles = []; | |
| let lastBubbleTime = 0; | |
| let touchStartX = 0; | |
| let touchStartY = 0; | |
| // Bubble types with properties | |
| const bubbleTypes = [ | |
| { color: '#ff9a9e', points: 10, radius: 30, speed: 2, frequency: 0.5 }, // Normal | |
| { color: '#a18cd1', points: 20, radius: 25, speed: 4, frequency: 0.2 }, // Speed | |
| { color: '#ffd166', points: 50, radius: 40, speed: 1.5, frequency: 0.15 }, // Bonus | |
| { color: '#06d6a0', points: 0, radius: 35, speed: 2, frequency: 0.15 } // Time (special) | |
| ]; | |
| // Initialize game | |
| function init() { | |
| canvas = document.getElementById('game-canvas'); | |
| ctx = canvas.getContext('2d'); | |
| // Set canvas dimensions | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| // Event listeners for buttons | |
| document.getElementById('start-btn').addEventListener('click', startGame); | |
| document.getElementById('pause-btn').addEventListener('click', togglePause); | |
| document.getElementById('restart-btn').addEventListener('click', restartGame); | |
| // Touch events for the canvas | |
| canvas.addEventListener('touchstart', handleTouchStart, { passive: false }); | |
| canvas.addEventListener('touchmove', handleTouchMove, { passive: false }); | |
| canvas.addEventListener('touchend', handleTouchEnd, { passive: false }); | |
| // Mouse events for desktop testing | |
| canvas.addEventListener('mousedown', handleMouseDown); | |
| // Draw initial screen | |
| drawStartScreen(); | |
| } | |
| // Resize canvas to fit container | |
| function resizeCanvas() { | |
| const container = canvas.parentElement; | |
| canvas.width = container.clientWidth; | |
| canvas.height = container.clientHeight; | |
| if (!gameActive) { | |
| drawStartScreen(); | |
| } | |
| } | |
| // Draw the start screen | |
| function drawStartScreen() { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw background gradient | |
| const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); | |
| gradient.addColorStop(0, 'rgba(26, 26, 46, 0.9)'); | |
| gradient.addColorStop(1, 'rgba(22, 33, 62, 0.9)'); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw title | |
| ctx.fillStyle = '#ffd166'; | |
| ctx.font = 'bold 40px Arial Rounded MT Bold, Arial, sans-serif'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText('BUBBLE POP FRENZY', canvas.width/2, canvas.height/2 - 40); | |
| // Draw instructions | |
| ctx.fillStyle = '#a1c4fd'; | |
| ctx.font = '20px Arial Rounded MT Bold, Arial, sans-serif'; | |
| ctx.fillText('Tap START to begin!', canvas.width/2, canvas.height/2 + 20); | |
| // Draw sample bubbles | |
| for (let i = 0; i < 5; i++) { | |
| const x = canvas.width * 0.2 + i * canvas.width * 0.15; | |
| const y = canvas.height * 0.7; | |
| const radius = 25 + i * 5; | |
| const color = bubbleTypes[i % bubbleTypes.length].color; | |
| drawBubble(x, y, radius, color); | |
| } | |
| } | |
| // Start the game | |
| function startGame() { | |
| if (gameActive && !gamePaused) return; | |
| if (!gameActive) { | |
| // Reset game state | |
| score = 0; | |
| timeLeft = 60; | |
| bubbles = []; | |
| lastBubbleTime = 0; | |
| // Update UI | |
| document.getElementById('score').textContent = score; | |
| document.getElementById('timer').textContent = timeLeft; | |
| document.getElementById('bubbles-count').textContent = bubbles.length; | |
| document.getElementById('game-over').style.display = 'none'; | |
| gameActive = true; | |
| gamePaused = false; | |
| document.getElementById('pause-btn').innerHTML = '<i class="fas fa-pause"></i> PAUSE'; | |
| } else if (gamePaused) { | |
| gamePaused = false; | |
| document.getElementById('pause-btn').innerHTML = '<i class="fas fa-pause"></i> PAUSE'; | |
| } | |
| // Start game loop | |
| gameLoop(); | |
| } | |
| // Toggle pause state | |
| function togglePause() { | |
| if (!gameActive) return; | |
| gamePaused = !gamePaused; | |
| if (gamePaused) { | |
| document.getElementById('pause-btn').innerHTML = '<i class="fas fa-play"></i> RESUME'; | |
| cancelAnimationFrame(animationId); | |
| } else { | |
| document.getElementById('pause-btn').innerHTML = '<i class="fas fa-pause"></i> PAUSE'; | |
| gameLoop(); | |
| } | |
| } | |
| // Restart the game | |
| function restartGame() { | |
| document.getElementById('game-over').style.display = 'none'; | |
| startGame(); | |
| } | |
| // Main game loop | |
| function gameLoop(timestamp) { | |
| if (!gameActive || gamePaused) return; | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw background | |
| drawBackground(); | |
| // Spawn new bubbles | |
| spawnBubbles(timestamp); | |
| // Update and draw bubbles | |
| updateBubbles(); | |
| // Update timer | |
| updateTimer(); | |
| // Update UI | |
| document.getElementById('score').textContent = score; | |
| document.getElementById('timer').textContent = Math.ceil(timeLeft); | |
| document.getElementById('bubbles-count').textContent = bubbles.length; | |
| // Check if game is over | |
| if (timeLeft <= 0) { | |
| endGame(); | |
| return; | |
| } | |
| // Continue game loop | |
| animationId = requestAnimationFrame(gameLoop); | |
| } | |
| // Draw the game background | |
| function drawBackground() { | |
| // Draw gradient background | |
| const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height); | |
| gradient.addColorStop(0, 'rgba(10, 15, 35, 0.9)'); | |
| gradient.addColorStop(1, 'rgba(5, 10, 25, 0.9)'); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw grid lines | |
| ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)'; | |
| ctx.lineWidth = 1; | |
| // Vertical lines | |
| for (let x = 0; x < canvas.width; x += 50) { | |
| ctx.beginPath(); | |
| ctx.moveTo(x, 0); | |
| ctx.lineTo(x, canvas.height); | |
| ctx.stroke(); | |
| } | |
| // Horizontal lines | |
| for (let y = 0; y < canvas.height; y += 50) { | |
| ctx.beginPath(); | |
| ctx.moveTo(0, y); | |
| ctx.lineTo(canvas.width, y); | |
| ctx.stroke(); | |
| } | |
| } | |
| // Spawn new bubbles | |
| function spawnBubbles(timestamp) { | |
| // Limit number of bubbles | |
| if (bubbles.length > 30) return; | |
| // Spawn new bubble occasionally | |
| if (timestamp - lastBubbleTime > 500) { // Every 500ms | |
| lastBubbleTime = timestamp; | |
| // Determine which type of bubble to spawn based on frequency | |
| const rand = Math.random(); | |
| let cumulativeFreq = 0; | |
| let selectedType = 0; | |
| for (let i = 0; i < bubbleTypes.length; i++) { | |
| cumulativeFreq += bubbleTypes[i].frequency; | |
| if (rand <= cumulativeFreq) { | |
| selectedType = i; | |
| break; | |
| } | |
| } | |
| const type = bubbleTypes[selectedType]; | |
| // Create new bubble | |
| const bubble = { | |
| x: Math.random() * (canvas.width - type.radius * 2) + type.radius, | |
| y: canvas.height + type.radius, // Start below the canvas | |
| radius: type.radius, | |
| color: type.color, | |
| points: type.points, | |
| speed: type.speed, | |
| vx: (Math.random() - 0.5) * 2, // Random horizontal velocity | |
| vy: -type.speed, // Move upward | |
| type: selectedType | |
| }; | |
| bubbles.push(bubble); | |
| } | |
| } | |
| // Update all bubbles | |
| function updateBubbles() { | |
| for (let i = bubbles.length - 1; i >= 0; i--) { | |
| const bubble = bubbles[i]; | |
| // Update position | |
| bubble.x += bubble.vx; | |
| bubble.y += bubble.vy; | |
| // Bounce off walls | |
| if (bubble.x - bubble.radius <= 0 || bubble.x + bubble.radius >= canvas.width) { | |
| bubble.vx *= -0.9; // Reverse direction with some damping | |
| bubble.x = Math.max(bubble.radius, Math.min(canvas.width - bubble.radius, bubble.x)); | |
| } | |
| // Bounce off ceiling | |
| if (bubble.y - bubble.radius <= 0) { | |
| bubble.vy *= -0.9; | |
| bubble.y = bubble.radius; | |
| } | |
| // Apply gravity | |
| bubble.vy += 0.05; | |
| // Remove bubbles that go below the screen | |
| if (bubble.y - bubble.radius > canvas.height + 50) { | |
| bubbles.splice(i, 1); | |
| continue; | |
| } | |
| // Draw bubble | |
| drawBubble(bubble.x, bubble.y, bubble.radius, bubble.color); | |
| } | |
| } | |
| // Draw a bubble | |
| function drawBubble(x, y, radius, color) { | |
| // Bubble shine effect | |
| const gradient = ctx.createRadialGradient( | |
| x - radius/3, y - radius/3, 1, | |
| x, y, radius | |
| ); | |
| gradient.addColorStop(0, 'rgba(255, 255, 255, 0.8)'); | |
| gradient.addColorStop(0.3, color); | |
| gradient.addColorStop(1, 'rgba(0, 0, 0, 0.3)'); | |
| // Draw bubble | |
| ctx.beginPath(); | |
| ctx.arc(x, y, radius, 0, Math.PI * 2); | |
| ctx.fillStyle = gradient; | |
| ctx.fill(); | |
| // Bubble border | |
| ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)'; | |
| ctx.lineWidth = 2; | |
| ctx.stroke(); | |
| // Bubble highlight | |
| ctx.beginPath(); | |
| ctx.arc(x - radius/3, y - radius/3, radius/4, 0, Math.PI * 2); | |
| ctx.fillStyle = 'rgba(255, 255, 255, 0.6)'; | |
| ctx.fill(); | |
| } | |
| // Update the game timer | |
| function updateTimer() { | |
| // Decrease time more slowly to make game last longer | |
| timeLeft -= 0.016; // About 60 FPS | |
| } | |
| // Handle touch start | |
| function handleTouchStart(e) { | |
| e.preventDefault(); | |
| const touch = e.touches[0]; | |
| touchStartX = touch.clientX; | |
| touchStartY = touch.clientY; | |
| // Get canvas position | |
| const rect = canvas.getBoundingClientRect(); | |
| const x = touch.clientX - rect.left; | |
| const y = touch.clientY - rect.top; | |
| checkBubblePop(x, y); | |
| } | |
| // Handle touch move | |
| function handleTouchMove(e) { | |
| e.preventDefault(); | |
| // For continuous popping while dragging finger | |
| const touch = e.touches[0]; | |
| const rect = canvas.getBoundingClientRect(); | |
| const x = touch.clientX - rect.left; | |
| const y = touch.clientY - rect.top; | |
| checkBubblePop(x, y); | |
| } | |
| // Handle touch end | |
| function handleTouchEnd(e) { | |
| e.preventDefault(); | |
| } | |
| // Handle mouse down (for desktop testing) | |
| function handleMouseDown(e) { | |
| const rect = canvas.getBoundingClientRect(); | |
| const x = e.clientX - rect.left; | |
| const y = e.clientY - rect.top; | |
| checkBubblePop(x, y); | |
| } | |
| // Check if a bubble was popped | |
| function checkBubblePop(x, y) { | |
| if (!gameActive || gamePaused) return; | |
| for (let i = bubbles.length - 1; i >= 0; i--) { | |
| const bubble = bubbles[i]; | |
| const distance = Math.sqrt((x - bubble.x) ** 2 + (y - bubble.y) ** 2); | |
| if (distance <= bubble.radius) { | |
| // Pop the bubble | |
| bubbles.splice(i, 1); | |
| // Add score | |
| score += bubble.points; | |
| // Special effect for time bubble | |
| if (bubble.type === 3) { // Time bubble | |
| timeLeft += 5; // Add 5 seconds | |
| // Visual feedback for time added | |
| ctx.fillStyle = '#06d6a0'; | |
| ctx.font = 'bold 24px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText('+5s', bubble.x, bubble.y - bubble.radius - 10); | |
| } else { | |
| // Visual feedback for points | |
| ctx.fillStyle = '#ffd166'; | |
| ctx.font = 'bold 24px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(`+${bubble.points}`, bubble.x, bubble.y - bubble.radius - 10); | |
| } | |
| // Create pop effect | |
| createPopEffect(bubble.x, bubble.y, bubble.color); | |
| break; // Only pop one bubble per touch | |
| } | |
| } | |
| } | |
| // Create a pop effect when a bubble is popped | |
| function createPopEffect(x, y, color) { | |
| // Draw several small circles expanding outward | |
| for (let i = 0; i < 8; i++) { | |
| const angle = (i / 8) * Math.PI * 2; | |
| const speed = 3; | |
| const particle = { | |
| x: x, | |
| y: y, | |
| radius: 5, | |
| color: color, | |
| vx: Math.cos(angle) * speed, | |
| vy: Math.sin(angle) * speed, | |
| life: 20 // frames | |
| }; | |
| // Draw particles immediately | |
| drawParticle(particle); | |
| // Animate particles (simplified - would need particle system for full effect) | |
| setTimeout(() => { | |
| if (ctx) { | |
| // Clear and redraw would be needed for proper animation | |
| // This is a simplified version for demo | |
| } | |
| }, 50); | |
| } | |
| } | |
| // Draw a particle | |
| function drawParticle(particle) { | |
| ctx.beginPath(); | |
| ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); | |
| ctx.fillStyle = particle.color; | |
| ctx.fill(); | |
| } | |
| // End the game | |
| function endGame() { | |
| gameActive = false; | |
| cancelAnimationFrame(animationId); | |
| // Show game over screen | |
| document.getElementById('final-score').textContent = score; | |
| document.getElementById('game-over').style.display = 'flex'; | |
| // Add pulsing animation to restart button | |
| document.getElementById('restart-btn').classList.add('pulse'); | |
| } | |
| // Initialize the game when page loads | |
| window.addEventListener('load', init); | |
| </script> | |
| </body> | |
| </html> |