forms

Bubble Pop Game

Highlights of this version:
Visual Polish: Uses radial gradients for the background, glassmorphism for the UI cards, and neon glow effects for the bubbles.

Smooth Physics: Bubbles float gently using CSS keyframes and explode into color-matched particles when clicked.

Mobile Friendly: Designed with touch-events in mind and a responsive layout that works on phones or desktops.

Game Loop: Includes a start menu, a 60-second timer, score tracking, and a game-over summary.

Code Snippet

<!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>Neon Bubble Pop</title>
    <style>
        :root {
            --bg-dark: #0f172a;
            --primary: #22d3ee;
            --secondary: #818cf8;
            --accent: #f43f5e;
            --text: #f8fafc;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            user-select: none;
            -webkit-tap-highlight-color: transparent;
        }

        body {
            font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            background-color: var(--bg-dark);
            color: var(--text);
            overflow: hidden;
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        /* UI Layers */
        #game-container {
            position: relative;
            flex-grow: 1;
            width: 100%;
            overflow: hidden;
            background: radial-gradient(circle at center, #1e293b 0%, #0f172a 100%);
            cursor: crosshair;
        }

        #hud {
            position: absolute;
            top: 20px;
            left: 0;
            right: 0;
            display: flex;
            justify-content: space-around;
            pointer-events: none;
            z-index: 10;
        }

        .stat-card {
            background: rgba(255, 255, 255, 0.05);
            backdrop-filter: blur(8px);
            padding: 10px 20px;
            border-radius: 15px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            text-align: center;
            min-width: 100px;
        }

        .stat-label {
            font-size: 0.7rem;
            text-transform: uppercase;
            letter-spacing: 1px;
            opacity: 0.7;
        }

        .stat-value {
            font-size: 1.5rem;
            font-weight: bold;
            color: var(--primary);
        }

        /* Bubble Styling */
        .bubble {
            position: absolute;
            border-radius: 50%;
            cursor: pointer;
            transition: transform 0.1s ease-out;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: inset -5px -5px 15px rgba(0,0,0,0.2),
                        inset 5px 5px 15px rgba(255,255,255,0.4);
        }

        .bubble::after {
            content: '';
            position: absolute;
            top: 15%;
            left: 15%;
            width: 25%;
            height: 25%;
            background: rgba(255, 255, 255, 0.6);
            border-radius: 50%;
        }

        /* Menus */
        .overlay {
            position: fixed;
            inset: 0;
            background: rgba(15, 23, 42, 0.9);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 100;
            transition: opacity 0.3s ease;
        }

        .menu-card {
            background: #1e293b;
            padding: 40px;
            border-radius: 24px;
            text-align: center;
            box-shadow: 0 0 40px rgba(0,0,0,0.5), 0 0 20px var(--primary);
            border: 1px solid var(--primary);
            max-width: 90%;
            width: 400px;
        }

        h1 {
            font-size: 2.5rem;
            margin-bottom: 20px;
            background: linear-gradient(to right, var(--primary), var(--secondary));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .btn {
            background: var(--primary);
            color: var(--bg-dark);
            border: none;
            padding: 15px 40px;
            font-size: 1.2rem;
            font-weight: bold;
            border-radius: 12px;
            cursor: pointer;
            transition: transform 0.2s, background 0.2s;
            margin-top: 20px;
        }

        .btn:hover {
            transform: scale(1.05);
            background: #06b6d4;
        }

        .hidden {
            display: none !important;
            opacity: 0;
        }

        /* Particle effects */
        .particle {
            position: absolute;
            pointer-events: none;
            border-radius: 50%;
            animation: explode 0.6s ease-out forwards;
        }

        @keyframes explode {
            0% { transform: scale(1); opacity: 1; }
            100% { transform: scale(0); opacity: 0; }
        }

        /* Float animation */
        @keyframes float {
            0%, 100% { transform: translateY(0); }
            50% { transform: translateY(-10px); }
        }
    </style>
</head>
<body>

    <div id="hud">
        <div class="stat-card">
            <div class="stat-label">Score</div>
            <div id="score-val" class="stat-value">0</div>
        </div>
        <div class="stat-card">
            <div class="stat-label">Time</div>
            <div id="time-val" class="stat-value">60</div>
        </div>
    </div>

    <div id="game-container"></div>

    <!-- Start Menu -->
    <div id="start-menu" class="overlay">
        <div class="menu-card">
            <h1>BUBBLE POP</h1>
            <p>Pop as many as you can in 60 seconds!</p>
            <button id="start-btn" class="btn">START GAME</button>
        </div>
    </div>

    <!-- Game Over Menu -->
    <div id="game-over" class="overlay hidden">
        <div class="menu-card">
            <h1>TIME'S UP!</h1>
            <p>Your Final Score:</p>
            <div id="final-score" class="stat-value" style="font-size: 4rem; margin: 20px 0;">0</div>
            <button id="restart-btn" class="btn">PLAY AGAIN</button>
        </div>
    </div>

    <script>
        const container = document.getElementById('game-container');
        const scoreEl = document.getElementById('score-val');
        const timeEl = document.getElementById('time-val');
        const startMenu = document.getElementById('start-menu');
        const gameOverMenu = document.getElementById('game-over');
        const finalScoreEl = document.getElementById('final-score');

        let score = 0;
        let timeLeft = 60;
        let gameActive = false;
        let spawnInterval;
        let timerInterval;

        const colors = [
            '#22d3ee', // cyan
            '#818cf8', // indigo
            '#f43f5e', // rose
            '#fbbf24', // amber
            '#34d399', // emerald
            '#a78bfa'  // violet
        ];

        function init() {
            document.getElementById('start-btn').addEventListener('click', startGame);
            document.getElementById('restart-btn').addEventListener('click', startGame);
        }

        function startGame() {
            score = 0;
            timeLeft = 60;
            gameActive = true;
            scoreEl.textContent = score;
            timeEl.textContent = timeLeft;
            
            startMenu.classList.add('hidden');
            gameOverMenu.classList.add('hidden');
            container.innerHTML = '';

            spawnInterval = setInterval(spawnBubble, 600);
            timerInterval = setInterval(updateTimer, 1000);
            
            // Spawn initial batch
            for(let i=0; i<5; i++) spawnBubble();
        }

        function updateTimer() {
            timeLeft--;
            timeEl.textContent = timeLeft;
            if (timeLeft <= 0) {
                endGame();
            }
        }

        function spawnBubble() {
            if (!gameActive) return;

            const bubble = document.createElement('div');
            const size = Math.random() * (80 - 40) + 40;
            const color = colors[Math.floor(Math.random() * colors.length)];
            
            bubble.className = 'bubble';
            bubble.style.width = `${size}px`;
            bubble.style.height = `${size}px`;
            bubble.style.backgroundColor = color;
            bubble.style.boxShadow = `0 0 20px ${color}44, inset -5px -5px 15px rgba(0,0,0,0.2), inset 5px 5px 15px rgba(255,255,255,0.4)`;
            
            const maxX = container.clientWidth - size;
            const maxY = container.clientHeight - size;
            
            bubble.style.left = `${Math.random() * maxX}px`;
            bubble.style.top = `${Math.random() * maxY}px`;
            
            // Random floating speed
            bubble.style.animation = `float ${3 + Math.random() * 2}s ease-in-out infinite`;

            bubble.addEventListener('mousedown', (e) => pop(bubble, color, e));
            bubble.addEventListener('touchstart', (e) => {
                e.preventDefault();
                pop(bubble, color, e.touches[0]);
            });

            container.appendChild(bubble);

            // Auto-remove bubble after some time if not popped
            setTimeout(() => {
                if (bubble.parentElement) {
                    bubble.style.transform = 'scale(0)';
                    bubble.style.opacity = '0';
                    setTimeout(() => bubble.remove(), 300);
                }
            }, 4000 + Math.random() * 2000);
        }

        function pop(el, color, event) {
            if (!gameActive) return;

            score += 10;
            scoreEl.textContent = score;

            // Create particles
            createParticles(event.clientX, event.clientY, color);

            // Pop animation
            el.style.transform = 'scale(1.4)';
            el.style.opacity = '0';
            el.style.pointerEvents = 'none';

            setTimeout(() => el.remove(), 100);
        }

        function createParticles(x, y, color) {
            for (let i = 0; i < 8; i++) {
                const p = document.createElement('div');
                p.className = 'particle';
                const size = Math.random() * 8 + 4;
                p.style.width = `${size}px`;
                p.style.height = `${size}px`;
                p.style.backgroundColor = color;
                p.style.left = `${x}px`;
                p.style.top = `${y}px`;
                
                const angle = Math.random() * Math.PI * 2;
                const velocity = Math.random() * 100 + 50;
                const tx = Math.cos(angle) * velocity;
                const ty = Math.sin(angle) * velocity;
                
                p.style.setProperty('--tx', `${tx}px`);
                p.style.setProperty('--ty', `${ty}px`);
                
                // Add movement logic
                p.animate([
                    { transform: 'translate(0, 0)', opacity: 1 },
                    { transform: `translate(${tx}px, ${ty}px)`, opacity: 0 }
                ], {
                    duration: 600,
                    easing: 'cubic-bezier(0, .9, .57, 1)',
                    fill: 'forwards'
                });

                container.appendChild(p);
                setTimeout(() => p.remove(), 600);
            }
        }

        function endGame() {
            gameActive = false;
            clearInterval(spawnInterval);
            clearInterval(timerInterval);
            
            finalScoreEl.textContent = score;
            gameOverMenu.classList.remove('hidden');
            
            // Clean up remaining bubbles
            const bubbles = document.querySelectorAll('.bubble');
            bubbles.forEach(b => b.remove());
        }

        window.onload = init;
    </script>
</body>
</html>

Media & Assets

Bubble Pop Game
Resource ID: #00038
Posted: December 26, 2025
© 2025 Your Code Library.