diff --git a/dev-journal.md b/dev-journal.md index 913b86e..e9f52c7 100644 --- a/dev-journal.md +++ b/dev-journal.md @@ -72,4 +72,30 @@ --- +--- + +### Session 4 - 2026-08-20 (Morning) +**Status:** ✅ Sound Effects System Added + +#### What was done: +- Created complete audio system using Web Audio API (no external assets needed) +- Added click sound effect (rising C-major arpeggio on each harvest) +- Added purchase sound effect (descending chime when buying buildings) +- Added achievement fanfare (ascending chord progression on unlock) +- Integrated audio into game.js, achievements.js, and index.html +- Audio context auto-initializes on game start; browsers handle suspension automatically + +#### Files modified/created: +- `js/audio.js` - New file with Audio system (click, purchase, achievement sounds via Web Audio API) +- `js/game.js` - Added Audio.init() in init(), Audio.playClick() in harvest(), Audio.playPurchase() in buyBuilding() +- `js/achievements.js` - Added Audio.playAchievement() on achievement unlock +- `index.html` - Added audio.js script tag + +#### Next goals: +- Mobile touch optimizations (tap feedback, responsive layout) +- Prestige/reset system with bonuses +- Consider adding more sound variations or music + +--- + *Journal auto-updated by Hermes Agent cron job (every 8 hours)* diff --git a/index.html b/index.html index 6f6d7aa..ef6a530 100644 --- a/index.html +++ b/index.html @@ -103,6 +103,7 @@ + diff --git a/js/achievements.js b/js/achievements.js index 8fad98d..d88279b 100644 --- a/js/achievements.js +++ b/js/achievements.js @@ -164,7 +164,10 @@ const Achievements = { if (achievement.condition(Game.state)) { Game.state.achievements.push(id); UI.showNotification(`Achievement: ${achievement.name}!`, 'achievement'); - + + // Audio feedback + Audio.playAchievement(); + // Particle effect for achievement unlock const countEl = document.querySelector('#achievements-tab h3'); if (countEl) { diff --git a/js/audio.js b/js/audio.js new file mode 100644 index 0000000..cfaeaf4 --- /dev/null +++ b/js/audio.js @@ -0,0 +1,119 @@ +/** + * Cosmic Garden - Audio System + * Web Audio API-based sound generation (no external assets needed) + */ + +const Audio = { + ctx: null, + enabled: true, + + /** + * Initialize audio context (must be called after user interaction) + */ + init() { + try { + window.AudioContext = window.AudioContext || window.webkitAudioContext; + this.ctx = new AudioContext(); + console.log('🔊 Audio system initialized'); + } catch (e) { + console.warn('Web Audio API not supported'); + this.enabled = false; + } + }, + + /** + * Resume audio context (for browsers that suspend it) + */ + resume() { + if (this.ctx && this.ctx.state === 'suspended') { + this.ctx.resume(); + } + }, + + /** + * Create a simple tone + */ + playTone(frequency, duration, type = 'sine', volume = 0.1) { + if (!this.enabled || !this.ctx) return; + + const osc = this.ctx.createOscillator(); + const gain = this.ctx.createGain(); + + osc.type = type; + osc.frequency.setValueAtTime(frequency, this.ctx.currentTime); + + gain.gain.setValueAtTime(volume, this.ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + duration); + + osc.connect(gain); + gain.connect(this.ctx.destination); + + osc.start(this.ctx.currentTime); + osc.stop(this.ctx.currentTime + duration); + }, + + /** + * Play a rising arpeggio (for clicks) + */ + playClick() { + if (!this.enabled) return; + this.resume(); + + const now = this.ctx.currentTime; + const notes = [523.25, 659.25, 783.99]; // C major chord + + notes.forEach((freq, i) => { + setTimeout(() => { + this.playTone(freq, 0.1, 'sine', 0.08); + }, i * 30); + }); + }, + + /** + * Play a purchase sound (descending chime) + */ + playPurchase() { + if (!this.enabled) return; + this.resume(); + + const notes = [783.99, 659.25, 523.25]; // G - E - C + + notes.forEach((freq, i) => { + setTimeout(() => { + this.playTone(freq, 0.15, 'triangle', 0.1); + }, i * 50); + }); + }, + + /** + * Play an achievement unlock fanfare + */ + playAchievement() { + if (!this.enabled) return; + this.resume(); + + const notes = [523.25, 659.25, 783.99, 1046.50]; // C - E - G - C (high) + + notes.forEach((freq, i) => { + setTimeout(() => { + this.playTone(freq, 0.2, 'square', 0.06); + }, i * 80); + }); + }, + + /** + * Play a notification sound + */ + playNotification() { + if (!this.enabled) return; + this.resume(); + + this.playTone(880, 0.1, 'sine', 0.08); + setTimeout(() => { + this.playTone(1100, 0.15, 'sine', 0.08); + }, 100); + } +}; + +// Make globally accessible +window.Audio = Audio; diff --git a/js/game.js b/js/game.js index d9fc4f5..e16b8f0 100644 --- a/js/game.js +++ b/js/game.js @@ -32,6 +32,7 @@ const Game = { this.setupEventListeners(); this.startGameLoop(); this.startAutoSave(); + Audio.init(); UI.init(); Stats.init(); console.log('🌌 Cosmic Garden initialized!'); @@ -98,7 +99,10 @@ const Game = { const amount = this.getClickPower(); this.addStardust(amount); this.state.totalClicks++; - + + // Audio feedback + Audio.playClick(); + // Visual feedback UI.showFloatingText(event.clientX, event.clientY, `+${this.formatNumber(amount)}`); Particles.createClickParticles(event.clientX, event.clientY, 8); @@ -181,7 +185,10 @@ const Game = { this.state.buildings[buildingId] = (this.state.buildings[buildingId] || 0) + 1; UI.showNotification(`Bought ${BUILDINGS[buildingId].name}!`, 'success'); - + + // Audio feedback + Audio.playPurchase(); + // Particle effect on building purchase const card = document.getElementById(`building-${buildingId}`); if (card) {