Audio & Game Dev
8-BIT RETRO SFX GENERATOR
Synthesize classic arcade and 8-bit game sound effects (Coin, Laser, Explosion, Jump, Power-Up) powered by Web Audio API synthesis.
Instant Sound Presets
Start Frequency
440 Hz
End Frequency (Sweep)
880 Hz
Decay Duration
0.25s
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
let audioCtx = null;
const canvas = document.getElementById('waveform-canvas');
const ctx = canvas.getContext('2d');
const toast = document.getElementById('toast');
const elWave = document.getElementById('select-wave');
const elFStart = document.getElementById('input-fstart');
const elFEnd = document.getElementById('input-fend');
const elDecay = document.getElementById('input-decay');
const jsOut = document.getElementById('js-code-out');
function showToast(msg) {
toast.textContent = msg;
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 2000);
}
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
}
function playSound() {
initAudio();
const wave = elWave.value;
const fStart = parseFloat(elFStart.value);
const fEnd = parseFloat(elFEnd.value);
const decay = parseFloat(elDecay.value);
const now = audioCtx.currentTime;
if (wave === 'noise') {
const bufferSize = audioCtx.sampleRate * decay;
const buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
const noise = audioCtx.createBufferSource();
noise.buffer = buffer;
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(1, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + decay);
noise.connect(gain);
gain.connect(audioCtx.destination);
noise.start(now);
} else {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = wave;
osc.frequency.setValueAtTime(fStart, now);
osc.frequency.exponentialRampToValueAtTime(Math.max(20, fEnd), now + decay);
gain.gain.setValueAtTime(1, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + decay);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now);
osc.stop(now + decay);
}
drawWaveVisual(fStart, fEnd, decay);
updateCodeSnippet();
}
function drawWaveVisual(fStart, fEnd, decay) {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const w = canvas.width;
const h = canvas.height;
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, w, h);
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = '#00ff66';
for (let i = 0; i < w; i++) {
const progress = i / w;
const freq = fStart + (fEnd - fStart) * progress;
const y = (h / 2) + Math.sin(progress * freq * 0.05) * ((1 - progress) * (h * 0.4));
if (i === 0) ctx.moveTo(i, y);
else ctx.lineTo(i, y);
}
ctx.stroke();
}
function updateCodeSnippet() {
const code = `const ctx = new AudioContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = '${elWave.value}';
osc.frequency.setValueAtTime(${elFStart.value}, ctx.currentTime);
osc.frequency.exponentialRampToValueAtTime(${elFEnd.value}, ctx.currentTime + ${elDecay.value});
gain.gain.setValueAtTime(1, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + ${elDecay.value});
osc.connect(gain); gain.connect(ctx.destination);
osc.start(); osc.stop(ctx.currentTime + ${elDecay.value});`;
jsOut.textContent = code;
}
document.querySelectorAll('.btn-preset').forEach(btn => {
btn.addEventListener('click', () => {
const sfx = btn.dataset.sfx;
if (sfx === 'coin') {
elWave.value = 'square'; elFStart.value = 987; elFEnd.value = 1318; elDecay.value = 0.2;
} else if (sfx === 'laser') {
elWave.value = 'sawtooth'; elFStart.value = 1400; elFEnd.value = 120; elDecay.value = 0.15;
} else if (sfx === 'explosion') {
elWave.value = 'noise'; elFStart.value = 200; elFEnd.value = 50; elDecay.value = 0.6;
} else if (sfx === 'jump') {
elWave.value = 'square'; elFStart.value = 150; elFEnd.value = 600; elDecay.value = 0.25;
} else if (sfx === 'powerup') {
elWave.value = 'triangle'; elFStart.value = 330; elFEnd.value = 1100; elDecay.value = 0.4;
} else if (sfx === 'random') {
const waves = ['square', 'sawtooth', 'triangle', 'noise'];
elWave.value = waves[Math.floor(Math.random() * waves.length)];
elFStart.value = Math.floor(Math.random() * 1800 + 100);
elFEnd.value = Math.floor(Math.random() * 1800 + 100);
elDecay.value = (Math.random() * 0.5 + 0.1).toFixed(2);
}
document.getElementById('val-fstart').textContent = elFStart.value + ' Hz';
document.getElementById('val-fend').textContent = elFEnd.value + ' Hz';
document.getElementById('val-decay').textContent = elDecay.value + 's';
playSound();
});
});
[elFStart, elFEnd, elDecay].forEach(el => {
el.addEventListener('input', () => {
document.getElementById('val-fstart').textContent = elFStart.value + ' Hz';
document.getElementById('val-fend').textContent = elFEnd.value + ' Hz';
document.getElementById('val-decay').textContent = elDecay.value + 's';
updateCodeSnippet();
});
});
document.getElementById('btn-play').addEventListener('click', playSound);
document.getElementById('btn-copy-js').addEventListener('click', () => {
navigator.clipboard.writeText(jsOut.textContent);
showToast('Web Audio Code copied!');
});
// Init canvas
drawWaveVisual(440, 880, 0.25);
updateCodeSnippet();