Skip to content

Instantly share code, notes, and snippets.

@lardratboy
Created June 16, 2026 20:32
Show Gist options
  • Select an option

  • Save lardratboy/2cb120b6b6c6d1ddabb7b2a847164aba to your computer and use it in GitHub Desktop.

Select an option

Save lardratboy/2cb120b6b6c6d1ddabb7b2a847164aba to your computer and use it in GitHub Desktop.
retro sound manager
// --- 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