APK FET
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.
<!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>