/** * Cosmic Garden - Audio System * Sound effects using Web Audio API (no external assets needed) */ const Audio = { ctx: null, /** * Initialize audio context */ init() { try { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); } catch (e) { console.log('Web Audio API not supported'); } }, /** * Resume audio context (needed for some browsers) */ resume() { if (this.ctx && this.ctx.state === 'suspended') { this.ctx.resume(); } }, /** * Play click sound - rising C-major arpeggio */ playClick() { if (!this.ctx) return; this.resume(); const now = this.ctx.currentTime; const notes = [523.25, 659.25, 783.99]; // C5, E5, G5 notes.forEach((freq, i) => { const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.connect(gain); gain.connect(this.ctx.destination); osc.frequency.value = freq; osc.type = 'sine'; gain.gain.setValueAtTime(0.1, now + i * 0.05); gain.gain.exponentialRampToValueAtTime(0.01, now + i * 0.05 + 0.15); osc.start(now + i * 0.05); osc.stop(now + i * 0.05 + 0.15); }); }, /** * Play purchase sound - descending chime */ playPurchase() { if (!this.ctx) return; this.resume(); const now = this.ctx.currentTime; const notes = [783.99, 659.25, 523.25]; // G5, E5, C5 notes.forEach((freq, i) => { const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.connect(gain); gain.connect(this.ctx.destination); osc.frequency.value = freq; osc.type = 'triangle'; gain.gain.setValueAtTime(0.15, now + i * 0.08); gain.gain.exponentialRampToValueAtTime(0.01, now + i * 0.08 + 0.2); osc.start(now + i * 0.08); osc.stop(now + i * 0.08 + 0.2); }); }, /** * Play achievement sound - ascending chord fanfare */ playAchievement() { if (!this.ctx) return; this.resume(); const now = this.ctx.currentTime; const chords = [ [523.25, 659.25, 783.99], // C major [659.25, 783.99, 987.77], // E major [783.99, 987.77, 1174.66] // G major ]; chords.forEach((chord, chordIndex) => { chord.forEach(freq => { const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.connect(gain); gain.connect(this.ctx.destination); osc.frequency.value = freq; osc.type = 'sine'; const startTime = now + chordIndex * 0.15; gain.gain.setValueAtTime(0.12, startTime); gain.gain.exponentialRampToValueAtTime(0.01, startTime + 0.3); osc.start(startTime); osc.stop(startTime + 0.3); }); }); }, /** * Play prestige sound - ethereal whoosh */ playPrestige() { if (!this.ctx) return; this.resume(); const now = this.ctx.currentTime; // Rising sweep const osc = this.ctx.createOscillator(); const gain = this.ctx.createGain(); osc.connect(gain); gain.connect(this.ctx.destination); osc.type = 'sine'; osc.frequency.setValueAtTime(200, now); osc.frequency.exponentialRampToValueAtTime(800, now + 0.5); gain.gain.setValueAtTime(0.2, now); gain.gain.exponentialRampToValueAtTime(0.01, now + 0.8); osc.start(now); osc.stop(now + 0.8); // Sparkle overlay for (let i = 0; i < 5; i++) { const sparkle = this.ctx.createOscillator(); const sparkleGain = this.ctx.createGain(); sparkle.connect(sparkleGain); sparkleGain.connect(this.ctx.destination); sparkle.frequency.value = 1200 + Math.random() * 800; sparkle.type = 'sine'; const startTime = now + 0.1 + i * 0.1; sparkleGain.gain.setValueAtTime(0.08, startTime); sparkleGain.gain.exponentialRampToValueAtTime(0.01, startTime + 0.2); sparkle.start(startTime); sparkle.stop(startTime + 0.2); } } }; window.Audio = Audio;