I’ll create an incredibly satisfying digital bubble wrap experience with realistic 3D bubbles, smooth animations, and programmatically generated pop sounds. This will be a fully interactive stress-relief tool with multiple features!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cosmic Bubble Pop - Interactive Adventure</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
position: relative;
height: 100vh;
background: #000;
}
/* Animated starfield background */
.starfield {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.star {
position: absolute;
background: white;
border-radius: 50%;
animation: twinkle 3s infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
.shooting-star {
position: absolute;
width: 2px;
height: 2px;
background: white;
box-shadow: 0 0 10px 2px rgba(255, 255, 255, 0.8);
animation: shoot 3s linear infinite;
}
@keyframes shoot {
from {
transform: translateX(0) translateY(0);
opacity: 1;
}
to {
transform: translateX(-500px) translateY(300px);
opacity: 0;
}
}
/* Main game container */
.game-container {
position: relative;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
}
/* Header with stats */
header {
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
padding: 1rem 2rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
z-index: 100;
position: relative;
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
}
.header-content {
max-width: 1400px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
h1 {
font-size: 2rem;
background: linear-gradient(135deg, #ff6ec7, #7873ff, #4fc3f7);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
display: flex;
align-items: center;
gap: 0.5rem;
text-shadow: 0 0 20px rgba(255, 110, 199, 0.5);
}
.stats {
display: flex;
gap: 2rem;
align-items: center;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
background: linear-gradient(135deg, #ff6ec7, #7873ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
transition: transform 0.3s ease;
}
.stat-value.pop-animation {
animation: statPop 0.3s ease;
}
@keyframes statPop {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.3); }
}
.stat-label {
font-size: 0.875rem;
color: rgba(255, 255, 255, 0.7);
text-transform: uppercase;
letter-spacing: 1px;
}
.combo-indicator {
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, #ff6ec7, #7873ff);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: bold;
opacity: 0;
transition: opacity 0.3s ease;
}
.combo-indicator.active {
opacity: 1;
animation: pulse 0.5s ease infinite;
}
@keyframes pulse {
0%, 100% { transform: translateX(-50%) scale(1); }
50% { transform: translateX(-50%) scale(1.1); }
}
.controls {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 50px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn-primary {
background: linear-gradient(135deg, #ff6ec7, #7873ff);
color: white;
box-shadow: 0 4px 15px rgba(255, 110, 199, 0.4);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
backdrop-filter: blur(5px);
}
.btn-power {
background: linear-gradient(135deg, #ffd700, #ffed4e);
color: #333;
box-shadow: 0 4px 15px rgba(255, 215, 0, 0.4);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
}
button:active {
transform: translateY(0);
}
/* Power-up indicator */
.power-up-indicator {
position: fixed;
top: 100px;
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, #ffd700, #ffed4e);
color: #333;
padding: 1rem 2rem;
border-radius: 50px;
font-weight: bold;
font-size: 1.2rem;
box-shadow: 0 10px 30px rgba(255, 215, 0, 0.5);
opacity: 0;
transition: opacity 0.3s ease;
z-index: 200;
}
.power-up-indicator.active {
opacity: 1;
animation: powerUpPulse 1s ease infinite;
}
@keyframes powerUpPulse {
0%, 100% { transform: translateX(-50%) scale(1); }
50% { transform: translateX(-50%) scale(1.05); }
}
/* Bubble container */
.bubble-container {
flex: 1;
padding: 2rem;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
}
.bubble-wrap {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
gap: 8px;
max-width: 1200px;
width: 100%;
padding: 2rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.bubble {
width: 60px;
height: 60px;
border-radius: 50%;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
animation: bubbleAppear 0.5s ease;
}
/* Regular bubble */
.bubble.regular {
background: radial-gradient(circle at 30% 30%,
rgba(255, 255, 255, 0.9) 0%,
rgba(255, 255, 255, 0.7) 10%,
rgba(200, 230, 255, 0.8) 30%,
rgba(150, 200, 255, 0.6) 60%,
rgba(100, 150, 255, 0.4) 100%);
box-shadow:
inset -5px -5px 10px rgba(0, 0, 0, 0.1),
inset 5px 5px 10px rgba(255, 255, 255, 0.5),
0 5px 15px rgba(100, 150, 255, 0.3);
}
/* Rainbow bubble - special */
.bubble.rainbow {
background: linear-gradient(135deg,
#ff0000 0%,
#ff7f00 14%,
#ffff00 28%,
#00ff00 42%,
#0000ff 56%,
#4b0082 70%,
#9400d3 84%,
#ff0000 100%);
background-size: 400% 400%;
animation: rainbowFlow 3s ease infinite;
box-shadow:
inset -5px -5px 10px rgba(0, 0, 0, 0.3),
inset 5px 5px 10px rgba(255, 255, 255, 0.4),
0 5px 20px rgba(255, 0, 255, 0.5);
}
@keyframes rainbowFlow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* Golden bubble - bonus points */
.bubble.golden {
background: radial-gradient(circle at 30% 30%,
#fff8dc 0%,
#ffd700 20%,
#ffb347 50%,
#ff8c00 100%);
box-shadow:
inset -5px -5px 10px rgba(0, 0, 0, 0.2),
inset 5px 5px 10px rgba(255, 255, 255, 0.5),
0 5px 20px rgba(255, 215, 0, 0.6);
}
.bubble.golden::after {
content: '★';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.5rem;
color: #fff;
text-shadow: 0 0 10px rgba(255, 215, 0, 0.8);
}
/* Cosmic bubble - special effect */
.bubble.cosmic {
background: radial-gradient(circle at 30% 30%,
#e0b0ff 0%,
#9370db 20%,
#6a0dad 50%,
#4b0082 100%);
box-shadow:
inset -5px -5px 10px rgba(0, 0, 0, 0.3),
inset 5px 5px 10px rgba(255, 255, 255, 0.3),
0 5px 20px rgba(147, 112, 219, 0.6);
}
.bubble.cosmic::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background: radial-gradient(circle at 70% 70%,
rgba(255, 255, 255, 0.8) 0%,
transparent 30%);
animation: cosmicPulse 2s ease infinite;
}
@keyframes cosmicPulse {
0%, 100% { transform: scale(1); opacity: 0.7; }
50% { transform: scale(1.1); opacity: 1; }
}
/* Explosive bubble - chain reaction */
.bubble.explosive {
background: radial-gradient(circle at 30% 30%,
#ff6b6b 0%,
#ff4444 20%,
#cc0000 50%,
#990000 100%);
box-shadow:
inset -5px -5px 10px rgba(0, 0, 0, 0.3),
inset 5px 5px 10px rgba(255, 255, 255, 0.3),
0 5px 20px rgba(255, 0, 0, 0.6);
}
.bubble.explosive::after {
content: '💥';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.2rem;
}
@keyframes bubbleAppear {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
.bubble::before {
content: '';
position: absolute;
top: 10%;
left: 20%;
width: 30%;
height: 30%;
background: radial-gradient(circle,
rgba(255, 255, 255, 0.9) 0%,
transparent 70%);
border-radius: 50%;
}
.bubble:hover {
transform: scale(1.1);
}
.bubble.popping {
animation: pop 0.4s ease forwards;
}
@keyframes pop {
0% {
transform: scale(1);
}
50% {
transform: scale(1.3);
opacity: 0.8;
}
100% {
transform: scale(0);
opacity: 0;
}
}
.bubble.regenerating {
animation: regenerate 0.5s ease forwards;
}
@keyframes regenerate {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* Screen shake effect */
.screen-shake {
animation: shake 0.5s ease;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
/* Particle effects */
.particle {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
pointer-events: none;
animation: particleFly 1s ease-out forwards;
}
@keyframes particleFly {
from {
transform: translate(0, 0) scale(1);
opacity: 1;
}
to {
transform: translate(var(--tx), var(--ty)) scale(0);
opacity: 0;
}
}
/* Ripple effect */
.ripple {
position: absolute;
border-radius: 50%;
pointer-events: none;
animation: rippleEffect 0.6s ease-out forwards;
}
@keyframes rippleEffect {
from {
width: 0;
height: 0;
opacity: 1;
}
to {
width: 100px;
height: 100px;
opacity: 0;
}
}
/* Floating text */
.floating-text {
position: absolute;
font-weight: bold;
font-size: 1.5rem;
color: white;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
pointer-events: none;
animation: floatUp 1s ease-out forwards;
}
@keyframes floatUp {
from {
transform: translateY(0);
opacity: 1;
}
to {
transform: translateY(-50px);
opacity: 0;
}
}
/* Chain reaction effect */
.chain-reaction {
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%;
background: radial-gradient(circle,
rgba(255, 100, 100, 0.8) 0%,
transparent 70%);
pointer-events: none;
animation: chainReaction 0.5s ease-out forwards;
}
@keyframes chainReaction {
from {
transform: scale(0);
opacity: 1;
}
to {
transform: scale(3);
opacity: 0;
}
}
/* Settings panel */
.settings-panel {
position: fixed;
right: -300px;
top: 50%;
transform: translateY(-50%);
width: 280px;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
border-radius: 20px 0 0 20px;
box-shadow: -5px 0 20px rgba(0, 0, 0, 0.5);
padding: 2rem;
transition: right 0.3s ease;
z-index: 200;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.settings-panel.open {
right: 0;
}
.settings-toggle {
position: absolute;
left: -40px;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 80px;
background: rgba(0, 0, 0, 0.8);
border-radius: 10px 0 0 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: -3px 0 10px rgba(0, 0, 0, 0.3);
color: white;
}
.setting-item {
margin-bottom: 1.5rem;
}
.setting-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: white;
}
input[type="range"] {
width: 100%;
height: 6px;
border-radius: 3px;
background: rgba(255, 255, 255, 0.2);
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #ff6ec7, #7873ff);
cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #ff6ec7, #7873ff);
cursor: pointer;
border: none;
}
.checkbox-wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
}
input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
}
/* Achievement notification */
.achievement {
position: fixed;
top: 100px;
right: -400px;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
padding: 1.5rem;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
gap: 1rem;
transition: right 0.5s ease;
z-index: 300;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.achievement.show {
right: 20px;
}
.achievement-icon {
font-size: 2rem;
}
.achievement-text h3 {
color: #ff6ec7;
margin-bottom: 0.25rem;
}
.achievement-text p {
color: rgba(255, 255, 255, 0.8);
font-size: 0.875rem;
}
/* Power-up bar */
.power-up-bar {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 1rem;
padding: 1rem;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
border-radius: 50px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 100;
}
.power-up-slot {
width: 60px;
height: 60px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
border: 2px solid rgba(255, 255, 255, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
}
.power-up-slot.active {
background: linear-gradient(135deg, #ffd700, #ffed4e);
border-color: #ffd700;
animation: powerUpGlow 1s ease infinite;
}
@keyframes powerUpGlow {
0%, 100% { box-shadow: 0 0 10px rgba(255, 215, 0, 0.5); }
50% { box-shadow: 0 0 20px rgba(255, 215, 0, 0.8); }
}
.power-up-slot:hover {
transform: scale(1.1);
}
@media (max-width: 768px) {
h1 {
font-size: 1.5rem;
}
.bubble {
width: 50px;
height: 50px;
}
.bubble-wrap {
gap: 6px;
padding: 1rem;
}
.settings-panel {
width: 250px;
}
.power-up-bar {
bottom: 10px;
padding: 0.5rem;
}
.power-up-slot {
width: 50px;
height: 50px;
font-size: 1.2rem;
}
}
</style>
</head>
<body>
<!-- Animated starfield background -->
<div class="starfield" id="starfield"></div>
<div class="game-container">
<header>
<div class="header-content">
<h1>
<span>🌌</span>
Cosmic Bubble Pop
</h1>
<div class="stats">
<div class="stat-item">
<div class="combo-indicator" id="comboIndicator">COMBO x2</div>
<div class="stat-value" id="popCount">0</div>
<div class="stat-label">Popped</div>
</div>
<div class="stat-item">
<div class="stat-value" id="scoreCount">0</div>
<div class="stat-label">Score</div>
</div>
<div class="stat-item">
<div class="stat-value" id="highScore">0</div>
<div class="stat-label">Best</div>
</div>
</div>
<div class="controls">
<button class="btn-primary" onclick="resetBubbles()">
🔄 Reset
</button>
<button class="btn-power" onclick="activatePowerUp()">
⚡ Power-Up
</button>
<button class="btn-secondary" onclick="toggleSound()">
🔊 Sound: <span id="soundStatus">ON</span>
</button>
</div>
</div>
</header>
<main class="bubble-container" id="gameContainer">
<div class="bubble-wrap" id="bubbleWrap"></div>
</main>
<!-- Power-up indicator -->
<div class="power-up-indicator" id="powerUpIndicator">
POWER-UP ACTIVE!
</div>
<!-- Power-up bar -->
<div class="power-up-bar">
<div class="power-up-slot" id="powerUp1" onclick="usePowerUp(1)">❄️</div>
<div class="power-up-slot" id="powerUp2" onclick="usePowerUp(2)">⚡</div>
<div class="power-up-slot" id="powerUp3" onclick="usePowerUp(3)">🌟</div>
<div class="power-up-slot" id="powerUp4" onclick="usePowerUp(4)">💎</div>
</div>
<!-- Settings panel -->
<div class="settings-panel" id="settingsPanel">
<div class="settings-toggle" onclick="toggleSettings()">
⚙️
</div>
<h3 style="margin-bottom: 1.5rem; color: white;">Settings</h3>
<div class="setting-item">
<label class="setting-label">Bubble Size</label>
<input type="range" id="bubbleSize" min="40" max="80" value="60"
oninput="updateBubbleSize(this.value)">
<span id="sizeValue" style="color: rgba(255, 255, 255, 0.7);">60px</span>
</div>
<div class="setting-item">
<label class="setting-label">Pop Volume</label>
<input type="range" id="volume" min="0" max="100" value="50"
oninput="updateVolume(this.value)">
<span id="volumeValue" style="color: rgba(255, 255, 255, 0.7);">50%</span>
</div>
<div class="setting-item">
<label class="setting-label">Special Bubble Chance</label>
<input type="range" id="specialChance" min="0" max="50" value="20"
oninput="updateSpecialChance(this.value)">
<span id="specialValue" style="color: rgba(255, 255, 255, 0.7);">20%</span>
</div>
<div class="setting-item">
<label class="setting-label">Screen Shake</label>
<div class="checkbox-wrapper">
<input type="checkbox" id="screenShake" checked
onchange="toggleScreenShake(this.checked)">
<label for="screenShake" style="color: rgba(255, 255, 255, 0.7);">Enable</label>
</div>
</div>
<div class="setting-item">
<label class="setting-label">Auto Regenerate</label>
<div class="checkbox-wrapper">
<input type="checkbox" id="autoRegenerate" checked
onchange="toggleAutoRegenerate(this.checked)">
<label for="autoRegenerate" style="color: rgba(255, 255, 255, 0.7);">Enable</label>
</div>
</div>
</div>
<!-- Achievement notification -->
<div class="achievement" id="achievement">
<div class="achievement-icon">🏆</div>
<div class="achievement-text">
<h3 id="achievementTitle">Achievement!</h3>
<p id="achievementDesc">You've popped 100 bubbles!</p>
</div>
</div>
</div>
<script>
// Game variables
let audioContext;
let soundEnabled = true;
let volume = 0.5;
let bubbleSize = 60;
let specialChance = 0.2;
let screenShakeEnabled = true;
let autoRegenerate = true;
// Game stats
let popCount = 0;
let score = 0;
let highScore = localStorage.getItem('cosmicHighScore') || 0;
let combo = 0;
let lastPopTime = 0;
let powerUpActive = false;
let powerUpType = null;
let powerUpTimer = null;
// Power-up states
let powerUps = {
1: { name: 'Freeze', icon: '❄️', active: false, cooldown: 0 },
2: { name: 'Lightning', icon: '⚡', active: false, cooldown: 0 },
3: { name: 'Starburst', icon: '🌟', active: false, cooldown: 0 },
4: { name: 'Diamond', icon: '💎', active: false, cooldown: 0 }
};
// Initialize starfield
function createStarfield() {
const starfield = document.getElementById('starfield');
const starCount = 200;
for (let i = 0; i < starCount; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.width = Math.random() * 3 + 'px';
star.style.height = star.style.width;
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.animationDelay = Math.random() * 3 + 's';
starfield.appendChild(star);
}
// Create shooting stars
setInterval(() => {
if (Math.random() > 0.7) {
const shootingStar = document.createElement('div');
shootingStar.className = 'shooting-star';
shootingStar.style.left = Math.random() * 100 + '%';
shootingStar.style.top = Math.random() * 50 + '%';
starfield.appendChild(shootingStar);
setTimeout(() => {
shootingStar.remove();
}, 3000);
}
}, 2000);
}
// Initialize audio context
function initAudio() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
}
// Generate pop sound with pitch variation
function playPopSound(bubbleType = 'regular') {
if (!soundEnabled || !audioContext) return;
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
// Different pitch for different bubble types
const baseFreq = {
'regular': 200,
'rainbow': 400,
'golden': 300,
'cosmic': 150,
'explosive': 250
};
oscillator.frequency.setValueAtTime(baseFreq[bubbleType] || 200, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(50, audioContext.currentTime + 0.1);
gainNode.gain.setValueAtTime(volume, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.1);
}
// Create bubble element
function createBubble() {
const bubble = document.createElement('div');
bubble.className = 'bubble';
bubble.style.width = bubbleSize + 'px';
bubble.style.height = bubbleSize + 'px';
// Determine bubble type
let bubbleType = 'regular';
const rand = Math.random();
if (rand < specialChance) {
const types = ['rainbow', 'golden', 'cosmic', 'explosive'];
bubbleType = types[Math.floor(Math.random() * types.length)];
}
bubble.classList.add(bubbleType);
bubble.dataset.type = bubbleType;
bubble.addEventListener('click', function(e) {
popBubble(this, e);
});
bubble.addEventListener('touchstart', function(e) {
e.preventDefault();
popBubble(this, e.touches[0]);
});
return bubble;
}
// Pop bubble
function popBubble(bubble, event) {
if (bubble.classList.contains('popping')) return;
initAudio();
const bubbleType = bubble.dataset.type || 'regular';
playPopSound(bubbleType);
bubble.classList.add('popping');
// Update combo
const currentTime = Date.now();
if (currentTime - lastPopTime < 1000) {
combo++;
updateCombo();
} else {
combo = 1;
updateCombo();
}
lastPopTime = currentTime;
// Calculate points
let points = 10;
switch (bubbleType) {
case 'rainbow':
points = 50;
createRainbowEffect(event.pageX, event.pageY);
break;
case 'golden':
points = 30;
createGoldenEffect(event.pageX, event.pageY);
break;
case 'cosmic':
points = 40;
createCosmicEffect(event.pageX, event.pageY);
break;
case 'explosive':
points = 20;
createExplosiveEffect(bubble, event.pageX, event.pageY);
break;
default:
createRegularEffect(event.pageX, event.pageY);
}
// Apply combo multiplier
points *= combo;
score += points;
// Update stats
popCount++;
updateStat('popCount', popCount);
updateStat('scoreCount', score);
// Update high score
if (score > highScore) {
highScore = score;
localStorage.setItem('cosmicHighScore', highScore);
updateStat('highScore', highScore);
}
// Create floating text
createFloatingText(event.pageX, event.pageY, `+${points}`);
// Check achievements
checkAchievements();
// Regenerate bubble
if (autoRegenerate) {
setTimeout(() => {
bubble.classList.remove('popping');
bubble.classList.add('regenerating');
setTimeout(() => {
bubble.classList.remove('regenerating');
}, 500);
}, 1000);
}
}
// Update combo display
function updateCombo() {
const indicator = document.getElementById('comboIndicator');
if (combo > 1) {
indicator.textContent = `COMBO x${combo}`;
indicator.classList.add('active');
} else {
indicator.classList.remove('active');
}
}
// Create regular pop effect
function createRegularEffect(x, y) {
createParticles(x, y, '#ffffff', 8);
createRipple(x, y, 'rgba(255, 255, 255, 0.5)');
}
// Create rainbow effect
function createRainbowEffect(x, y) {
const colors = ['#ff0000', '#ff7f00', '#ffff00', '#00ff00', '#0000ff', '#4b0082', '#9400d3'];
colors.forEach(color => {
createParticles(x, y, color, 5);
});
createRipple(x, y, 'rgba(255, 0, 255, 0.7)');
if (screenShakeEnabled) {
document.body.classList.add('screen-shake');
setTimeout(() => {
document.body.classList.remove('screen-shake');
}, 500);
}
}
// Create golden effect
function createGoldenEffect(x, y) {
createParticles(x, y, '#ffd700', 12);
createRipple(x, y, 'rgba(255, 215, 0, 0.7)');
createFloatingText(x, y, 'BONUS!');
}
// Create cosmic effect
function createCosmicEffect(x, y) {
createParticles(x, y, '#9370db', 10);
createRipple(x, y, 'rgba(147, 112, 219, 0.7)');
// Create additional cosmic particles
for (let i = 0; i < 5; i++) {
setTimeout(() => {
const px = x + (Math.random() - 0.5) * 100;
const py = y + (Math.random() - 0.5) * 100;
createParticles(px, py, '#e0b0ff', 3);
}, i * 100);
}
}
// Create explosive effect
function createExplosiveEffect(bubble, x, y) {
createParticles(x, y, '#ff4444', 15);
createRipple(x, y, 'rgba(255, 0, 0, 0.7)');
// Create chain reaction
const chainReaction = document.createElement('div');
chainReaction.className = 'chain-reaction';
chainReaction.style.left = (x - 50) + 'px';
chainReaction.style.top = (y - 50) + 'px';
document.body.appendChild(chainReaction);
setTimeout(() => {
chainReaction.remove();
}, 500);
// Pop nearby bubbles
const bubbles = document.querySelectorAll('.bubble:not(.popping)');
const rect = bubble.getBoundingClientRect();
bubbles.forEach(b => {
const bRect = b.getBoundingClientRect();
const distance = Math.sqrt(
Math.pow(rect.left - bRect.left, 2) +
Math.pow(rect.top - bRect.top, 2)
);
if (distance < 150 && b !== bubble) {
setTimeout(() => {
const event = new MouseEvent('click', {
clientX: bRect.left + bRect.width / 2,
clientY: bRect.top + bRect.height / 2
});
b.dispatchEvent(event);
}, 100);
}
});
if (screenShakeEnabled) {
document.body.classList.add('screen-shake');
setTimeout(() => {
document.body.classList.remove('screen-shake');
}, 500);
}
}
// Create particles
function createParticles(x, y, color, count) {
for (let i = 0; i < count; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.background = color;
const angle = (Math.PI * 2 * i) / count;
const distance = 50 + Math.random() * 50;
const tx = Math.cos(angle) * distance;
const ty = Math.sin(angle) * distance;
particle.style.setProperty('--tx', tx + 'px');
particle.style.setProperty('--ty', ty + 'px');
particle.style.left = x + 'px';
particle.style.top = y + 'px';
document.body.appendChild(particle);
setTimeout(() => {
particle.remove();
}, 1000);
}
}
// Create ripple effect
function createRipple(x, y, color) {
const ripple = document.createElement('div');
ripple.className = 'ripple';
ripple.style.background = `radial-gradient(circle, ${color} 0%, transparent 70%)`;
ripple.style.left = (x - 50) + 'px';
ripple.style.top = (y - 50) + 'px';
document.body.appendChild(ripple);
setTimeout(() => {
ripple.remove();
}, 600);
}
// Create floating text
function createFloatingText(x, y, text) {
const floatingText = document.createElement('div');
floatingText.className = 'floating-text';
floatingText.textContent = text;
floatingText.style.left = x + 'px';
floatingText.style.top = y + 'px';
document.body.appendChild(floatingText);
setTimeout(() => {
floatingText.remove();
}, 1000);
}
// Update stat with animation
function updateStat(statId, value) {
const element = document.getElementById(statId);
element.textContent = value;
element.classList.add('pop-animation');
setTimeout(() => {
element.classList.remove('pop-animation');
}, 300);
}
// Check achievements
function checkAchievements() {
const achievements = [
{ count: 10, title: 'First Steps!', desc: 'You popped 10 bubbles!' },
{ count: 50, title: 'Bubble Master!', desc: 'You popped 50 bubbles!' },
{ count: 100, title: 'Bubble Legend!', desc: 'You popped 100 bubbles!' },
{ count: 500, title: 'Bubble God!', desc: 'You popped 500 bubbles!' },
{ count: 1000, title: 'Unstoppable!', desc: 'You popped 1000 bubbles!' }
];
achievements.forEach(achievement => {
if (popCount === achievement.count) {
showAchievement(achievement.title, achievement.desc);
}
});
// Score achievements
if (score === 500) {
showAchievement('Score Master!', 'You reached 500 points!');
}
if (score === 1000) {
showAchievement('Score Legend!', 'You reached 1000 points!');
}
// Combo achievements
if (combo === 10) {
showAchievement('Combo King!', '10x combo achieved!');
}
if (combo === 20) {
showAchievement('Combo God!', '20x combo achieved!');
}
}
// Show achievement notification
function showAchievement(title, desc) {
const achievement = document.getElementById('achievement');
document.getElementById('achievementTitle').textContent = title;
document.getElementById('achievementDesc').textContent = desc;
achievement.classList.add('show');
setTimeout(() => {
achievement.classList.remove('show');
}, 3000);
}
// Activate power-up
function activatePowerUp() {
if (powerUpActive) return;
const availablePowerUps = Object.keys(powerUps).filter(key => !powerUps[key].active && powerUps[key].cooldown === 0);
if (availablePowerUps.length === 0) return;
const randomPowerUp = availablePowerUps[Math.floor(Math.random() * availablePowerUps.length)];
usePowerUp(parseInt(randomPowerUp));
}
// Use specific power-up
function usePowerUp(powerUpId) {
if (powerUps[powerUpId].active || powerUps[powerUpId].cooldown > 0) return;
powerUpActive = true;
powerUpType = powerUpId;
powerUps[powerUpId].active = true;
document.getElementById(`powerUp${powerUpId}`).classList.add('active');
document.getElementById('powerUpIndicator').classList.add('active');
// Apply power-up effect
switch (powerUpId) {
case 1: // Freeze
freezeAllBubbles();
break;
case 2: // Lightning
lightningStrike();
break;
case 3: // Starburst
starburst();
break;
case 4: // Diamond
doublePoints();
break;
}
// Set cooldown
setTimeout(() => {
powerUpActive = false;
powerUpType = null;
powerUps[powerUpId].active = false;
powerUps[powerUpId].cooldown = 10;
document.getElementById(`powerUp${powerUpId}`).classList.remove('active');
document.getElementById('powerUpIndicator').classList.remove('active');
// Cooldown countdown
const cooldownInterval = setInterval(() => {
powerUps[powerUpId].cooldown--;
if (powerUps[powerUpId].cooldown <= 0) {
clearInterval(cooldownInterval);
}
}, 1000);
}, 5000);
}
// Power-up effects
function freezeAllBubbles() {
const bubbles = document.querySelectorAll('.bubble:not(.popping)');
bubbles.forEach(bubble => {
bubble.style.animationPlayState = 'paused';
bubble.style.filter = 'hue-rotate(200deg)';
});
setTimeout(() => {
bubbles.forEach(bubble => {
bubble.style.animationPlayState = 'running';
bubble.style.filter = 'none';
});
}, 5000);
}
function lightningStrike() {
const bubbles = document.querySelectorAll('.bubble:not(.popping)');
const randomBubbles = Array.from(bubbles).sort(() => 0.5 - Math.random()).slice(0, 10);
randomBubbles.forEach((bubble, index) => {
setTimeout(() => {
const rect = bubble.getBoundingClientRect();
createLightningEffect(rect.left + rect.width / 2, rect.top + rect.height / 2);
const event = new MouseEvent('click', {
clientX: rect.left + rect.width / 2,
clientY: rect.top + rect.height / 2
});
bubble.dispatchEvent(event);
}, index * 100);
});
}
function createLightningEffect(x, y) {
const lightning = document.createElement('div');
lightning.style.position = 'absolute';
lightning.style.left = x + 'px';
lightning.style.top = '0px';
lightning.style.width = '2px';
lightning.style.height = y + 'px';
lightning.style.background = 'linear-gradient(to bottom, transparent, #ffffff, transparent)';
lightning.style.boxShadow = '0 0 10px #ffffff';
lightning.style.zIndex = '1000';
document.body.appendChild(lightning);
setTimeout(() => {
lightning.remove();
}, 200);
}
function starburst() {
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
for (let i = 0; i < 20; i++) {
const angle = (Math.PI * 2 * i) / 20;
const distance = 200 + Math.random() * 200;
const x = centerX + Math.cos(angle) * distance;
const y = centerY + Math.sin(angle) * distance;
createParticles(x, y, '#ffff00', 5);
createRipple(x, y, 'rgba(255, 255, 0, 0.7)');
}
if (screenShakeEnabled) {
document.body.classList.add('screen-shake');
setTimeout(() => {
document.body.classList.remove('screen-shake');
}, 500);
}
}
function doublePoints() {
// Visual indicator for double points
const indicator = document.createElement('div');
indicator.style.position = 'fixed';
indicator.style.top = '50%';
indicator.style.left = '50%';
indicator.style.transform = 'translate(-50%, -50%)';
indicator.style.fontSize = '3rem';
indicator.style.fontWeight = 'bold';
indicator.style.color = '#ffd700';
indicator.style.textShadow = '0 0 20px rgba(255, 215, 0, 0.8)';
indicator.style.zIndex = '1000';
indicator.textContent = '2X POINTS!';
document.body.appendChild(indicator);
setTimeout(() => {
indicator.remove();
}, 2000);
}
// Initialize bubbles
function initBubbles() {
const bubbleWrap = document.getElementById('bubbleWrap');
const bubbleCount = window.innerWidth < 768 ? 48 : 96;
for (let i = 0; i < bubbleCount; i++) {
bubbleWrap.appendChild(createBubble());
}
// Initialize high score display
updateStat('highScore', highScore);
}
// Reset all bubbles
function resetBubbles() {
const bubbleWrap = document.getElementById('bubbleWrap');
bubbleWrap.innerHTML = '';
popCount = 0;
score = 0;
combo = 0;
updateStat('popCount', 0);
updateStat('scoreCount', 0);
updateCombo();
initBubbles();
}
// Toggle sound
function toggleSound() {
soundEnabled = !soundEnabled;
document.getElementById('soundStatus').textContent = soundEnabled ? 'ON' : 'OFF';
}
// Toggle settings panel
function toggleSettings() {
document.getElementById('settingsPanel').classList.toggle('open');
}
// Update bubble size
function updateBubbleSize(size) {
bubbleSize = parseInt(size);
document.getElementById('sizeValue').textContent = size + 'px';
const bubbles = document.querySelectorAll('.bubble');
bubbles.forEach(bubble => {
bubble.style.width = size + 'px';
bubble.style.height = size + 'px';
});
}
// Update volume
function updateVolume(value) {
volume = value / 100;
document.getElementById('volumeValue').textContent = value + '%';
}
// Update special chance
function updateSpecialChance(value) {
specialChance = value / 100;
document.getElementById('specialValue').textContent = value + '%';
}
// Toggle screen shake
function toggleScreenShake(enabled) {
screenShakeEnabled = enabled;
}
// Toggle auto regenerate
function toggleAutoRegenerate(enabled) {
autoRegenerate = enabled;
}
// Initialize on load
window.addEventListener('load', () => {
createStarfield();
initBubbles();
});
// Handle window resize
window.addEventListener('resize', () => {
const bubbleWrap = document.getElementById('bubbleWrap');
const currentBubbles = bubbleWrap.children.length;
const targetBubbles = window.innerWidth < 768 ? 48 : 96;
if (currentBubbles < targetBubbles) {
for (let i = currentBubbles; i < targetBubbles; i++) {
bubbleWrap.appendChild(createBubble());
}
}
});
</script>
</body>
</html>
