Created
June 16, 2026 20:32
-
-
Save lardratboy/2cb120b6b6c6d1ddabb7b2a847164aba to your computer and use it in GitHub Desktop.
retro sound manager
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // --- RETRO SOUND MANAGER --- | |
| class RetroSoundManager { | |
| constructor() { | |
| this.audioContext = null; | |
| this.masterGain = null; | |
| this.enabled = true; | |
| this.initAudio(); | |
| } | |
| initAudio() { | |
| try { | |
| this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| this.masterGain = this.audioContext.createGain(); | |
| this.masterGain.gain.value = 0.3; // Master volume | |
| this.masterGain.connect(this.audioContext.destination); | |
| } catch (e) { | |
| console.warn('Web Audio API not supported'); | |
| this.enabled = false; | |
| } | |
| } | |
| // Resume audio context on user interaction (required by browsers) | |
| resume() { | |
| if (this.audioContext && this.audioContext.state === 'suspended') { | |
| this.audioContext.resume(); | |
| } | |
| } | |
| // Toggle sound on/off | |
| setEnabled(enabled) { | |
| this.enabled = enabled; | |
| } | |
| isEnabled() { | |
| return this.enabled; | |
| } | |
| // Create a classic chip-tune style sound | |
| playTone(frequency, duration, waveType = 'square', volume = 0.1) { | |
| if (!this.enabled || !this.audioContext) return; | |
| this.resume(); | |
| const oscillator = this.audioContext.createOscillator(); | |
| const gainNode = this.audioContext.createGain(); | |
| oscillator.type = waveType; | |
| oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime); | |
| // Classic envelope: quick attack, sustain, quick release | |
| gainNode.gain.setValueAtTime(0, this.audioContext.currentTime); | |
| gainNode.gain.linearRampToValueAtTime(volume, this.audioContext.currentTime + 0.01); | |
| gainNode.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration); | |
| oscillator.connect(gainNode); | |
| gainNode.connect(this.masterGain); | |
| oscillator.start(); | |
| oscillator.stop(this.audioContext.currentTime + duration); | |
| } | |
| // Sweep effect for explosions | |
| playSweep(startFreq, endFreq, duration, waveType = 'sawtooth', volume = 0.15) { | |
| if (!this.enabled || !this.audioContext) return; | |
| this.resume(); | |
| const oscillator = this.audioContext.createOscillator(); | |
| const gainNode = this.audioContext.createGain(); | |
| oscillator.type = waveType; | |
| oscillator.frequency.setValueAtTime(startFreq, this.audioContext.currentTime); | |
| oscillator.frequency.exponentialRampToValueAtTime(endFreq, this.audioContext.currentTime + duration); | |
| gainNode.gain.setValueAtTime(volume, this.audioContext.currentTime); | |
| gainNode.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration); | |
| oscillator.connect(gainNode); | |
| gainNode.connect(this.masterGain); | |
| oscillator.start(); | |
| oscillator.stop(this.audioContext.currentTime + duration); | |
| } | |
| // Multi-tone chord for special events | |
| playChord(frequencies, duration, waveType = 'square', volume = 0.08) { | |
| frequencies.forEach((freq, index) => { | |
| setTimeout(() => this.playTone(freq, duration, waveType, volume), index * 50); | |
| }); | |
| } | |
| // Sound effects for game events | |
| hover() { | |
| this.playTone(200, 0.03, 'square', 0.03); // Raised pitch, halved volume and duration | |
| } | |
| click() { | |
| this.playTone(600, 0.15, 'square', 0.1); | |
| } | |
| explode(delay = 0, waveIndex = 0) { | |
| // Create an ascending melody based on wave distance | |
| const scale = [220, 247, 277, 311, 349, 392, 440, 494]; // A minor scale | |
| const noteIndex = Math.min(waveIndex, scale.length - 1); | |
| const frequency = scale[noteIndex]; | |
| setTimeout(() => { | |
| // Play just the melodic note | |
| this.playTone(frequency, 0.4, 'triangle', 0.08); // Pure melodic component | |
| }, delay); | |
| } | |
| collect() { | |
| this.playChord([523, 659, 784, 1047], 0.4, 'triangle', 0.06); // C major arpeggio | |
| } | |
| gravity() { | |
| this.playTone(220, 0.8, 'triangle', 0.08); | |
| setTimeout(() => this.playTone(196, 0.6, 'triangle', 0.06), 100); | |
| } | |
| columnRemove() { | |
| this.playSweep(400, 120, 0.2, 'triangle', 0.12); // Quick downward "foop" | |
| } | |
| rotate() { | |
| this.playChord([440, 554, 659], 0.3, 'square', 0.06); | |
| } | |
| gameOver() { | |
| const notes = [330, 294, 262, 220, 196]; // Descending scale | |
| notes.forEach((freq, index) => { | |
| setTimeout(() => this.playTone(freq, 0.5, 'triangle', 0.1), index * 200); | |
| }); | |
| } | |
| victory() { | |
| const victoryMelody = [523, 659, 784, 1047, 1319]; // Ascending C major | |
| victoryMelody.forEach((freq, index) => { | |
| setTimeout(() => this.playTone(freq, 0.3, 'square', 0.08), index * 150); | |
| }); | |
| } | |
| newLevel() { | |
| this.playChord([262, 330, 392, 523], 0.8, 'triangle', 0.07); // C major chord | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment