let bubbles = []; let score = 0; let magicBubble = null; let gameStartTime = 0; let particles = []; let colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57', '#FF9FF3', '#54A0FF']; let faceColors = ['#FF9FF3', '#54A0FF', '#FF6B6B', '#4ECDC4', '#FFD93D', '#C7CEEA', '#FFB6C1']; // pastel neon for the face lines let bubblePopSounds = []; let magicPopSound; let audioUnlocked = false; function setup() { createCanvas(1024, 768); createSounds(); gameStartTime = millis(); spawnBubbles(); } function createSounds() { // Normal bubble pops (3 variations) - all the same good click for (let i = 0; i < 3; i++) { bubblePopSounds.push({ play: function() { let osc = new p5.Oscillator('sine'); let env = new p5.Envelope(); env.setADSR(0.001, 0.02, 0, 0.01); osc.freq(150); // Fixed frequency - always the same osc.amp(env); osc.start(); env.play(osc, 0, 0.05); setTimeout(() => osc.stop(), 20); } }); } // Magic bubble sound (still special but shorter) magicPopSound = { play: function() { for (let i = 0; i < 2; i++) { setTimeout(() => { let osc = new p5.Oscillator('sine'); let env = new p5.Envelope(); env.setADSR(0.001, 0.04, 0, 0.02); osc.freq(300 + 2 * 100); osc.amp(env); osc.start(); env.play(osc, 0, 0.1 ); setTimeout(() => osc.stop(), 20); }, i * 20); } } }; // No bubble rise sound - removed } function draw() { background(10, 10, 30); for (let i = bubbles.length - 1; i >= 0; i--) { let b = bubbles[i]; b.update(); b.display(); if (b.pos.y < -100) bubbles.splice(i, 1); } if (magicBubble) { magicBubble.update(); magicBubble.display(); if (magicBubble.pos.y < -100) magicBubble = null; } else if (millis() - gameStartTime > 20000 && random() < 0.001) { spawnMagicBubble(); } for (let i = particles.length - 1; i >= 0; i--) { particles[i].update(); particles[i].display(); if (particles[i].lifespan <= 0) particles.splice(i, 1); } fill(255); textSize(24); text(`Score: ${score}`, 20, 40); fill(180); textSize(14); text("Click bubbles • Red magic bubble clears everything!", 20, height - 20); } function mousePressed() { if (!audioUnlocked) { userStartAudio(); audioUnlocked = true; } if (magicBubble && dist(mouseX, mouseY, magicBubble.pos.x, magicBubble.pos.y) < magicBubble.size / 2) { magicPopSound.play(); explodeAll(); return; } for (let i = bubbles.length - 1; i >= 0; i--) { let b = bubbles[i]; if (dist(mouseX, mouseY, b.pos.x, b.pos.y) < b.size / 2) { popBubble(b, i); random(bubblePopSounds).play(); return; } } } function spawnBubbles() { for (let i = 0; i < 20; i++) bubbles.push(new Bubble()); setTimeout(spawnBubbles, 3000); } function spawnMagicBubble() { magicBubble = new Bubble(random(width), height + 50, '#FF0044', random(30, 50), true); magicBubble.vel.y = random(-1.2, -0.6); } function popBubble(bubble, index) { score += 10; createParticles(bubble.pos.x, bubble.pos.y, bubble.col); bubbles.splice(index, 1); } function explodeAll() { score += bubbles.length * 50 + 1000; for (let b of bubbles) createParticles(b.pos.x, b.pos.y, b.col); bubbles = []; if (magicBubble) { createParticles(magicBubble.pos.x, magicBubble.pos.y, '#FF0044'); magicBubble = null; } } function createParticles(x, y, col) { for (let i = 0; i < 12; i++) particles.push(new Particle(x, y, col)); } // =============================================== // NEW BUBBLE WITH "DEAD / DRUNK" FACE DRAWING // =============================================== class Bubble { constructor(x = random(width), y = height + random(50), col = random(colors), size = random(25, 45), isMagic = false) { this.pos = createVector(x, y); this.vel = createVector(random(-0.5, 0.5), random(-8, -1)); this.col = col; this.size = size; this.isMagic = isMagic; this.faceColor = random(faceColors); // random pastel neon for the face this.wobblePhase = random(TWO_PI); // for slight deformation animation } update() { this.pos.add(this.vel); this.vel.y *= 0.99; this.wobblePhase += 0.08; } display() { push(); translate(this.pos.x, this.pos.y); // Magic glow if (this.isMagic) { drawingContext.shadowBlur = 40; drawingContext.shadowColor = this.col; } // Main bubble circle (semi-transparent for that bubbly look) noStroke(); fill(this.col); ellipse(0, 0, this.size); // === DRAW THE CUTE X-EYES + WOBBLY MOUTH FACE === let s = this.size; let faceCol = this.faceColor; stroke(faceCol); strokeWeight(s * 0.09); noFill(); // Left X eye let offset = sin(this.wobblePhase) * 2; line(-s*0.25 + offset, -s*0.18, -s*0.12 + offset, -s*0.05); line(-s*0.12 + offset, -s*0.18, -s*0.25 + offset, -s*0.05); // Right X eye line(s*0.25 - offset, -s*0.18, s*0.12 - offset, -s*0.05); line(s*0.12 - offset, -s*0.18, s*0.25 - offset, -s*0.05); // Wobbly droopy mouth beginShape(); noFill(); strokeWeight(s * 0.07); for (let a = PI*0.2; a <= PI*0.8; a += 0.1) { let wave = sin(a*6 + this.wobblePhase*3) * s*0.04; let xx = cos(a) * s*0.32; let yy = sin(a) * s*0.22 + wave + s*0.15; vertex(xx, yy); } endShape(); // Tiny blush circles (optional cute touch) noStroke(); fill(red(faceCol), green(faceCol), blue(faceCol), 80); ellipse(-s*0.3, s*0.05, s*0.14); ellipse( s*0.3, s*0.05, s*0.14); // Reset shadow if (this.isMagic) drawingContext.shadowBlur = 0; pop(); } } class Particle { constructor(x, y, col) { this.pos = createVector(x, y); this.vel = p5.Vector.random2D().mult(random(3, 7)); this.lifespan = 255; this.col = col; this.size = random(3, 7); } update() { this.pos.add(this.vel); this.vel.mult(0.95); this.lifespan -= 12; } display() { noStroke(); fill(red(this.col), green(this.col), blue(this.col), this.lifespan); ellipse(this.pos.x, this.pos.y, this.size); } }