🎵 Mini Guida: Web Audio API

Introduzione alla sintesi e manipolazione del suono con JavaScript

0. Teoria: La Programmazione Musicale nel Browser

La programmazione musicale è l’arte di generare, modificare e controllare il suono usando procedure informatiche. Con JavaScript, grazie alla Web Audio API, possiamo scrivere programmi che diventano veri e propri strumenti virtuali.

🔸 Concetti chiave

🎶 Cosa si può fare

La Web Audio API è quindi una piattaforma per la musica generativa, la didattica del suono e la creatività web.

1. Creare un AudioContext

Tutto parte da un AudioContext, la “centralina” del suono.

const audioCtx = new AudioContext();

2. Generare un’onda semplice

const osc = audioCtx.createOscillator(); osc.type = 'sine'; osc.frequency.value = 440; osc.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + 1);

3. Controllare il volume (GainNode)

const osc2 = audioCtx.createOscillator(); const gain = audioCtx.createGain(); osc2.connect(gain); gain.connect(audioCtx.destination); gain.gain.value = 0.2; osc2.frequency.value = 220; osc2.start(); osc2.stop(audioCtx.currentTime + 1.5);

4. Applicare un filtro

const osc3 = audioCtx.createOscillator(); const filter = audioCtx.createBiquadFilter(); const gain2 = audioCtx.createGain(); filter.type = 'lowpass'; filter.frequency.value = 800; gain2.gain.value = 0.3; osc3.connect(filter); filter.connect(gain2); gain2.connect(audioCtx.destination); osc3.frequency.value = 600; osc3.start(); osc3.stop(audioCtx.currentTime + 2);

5. Rumore bianco (white noise)

const bufferSize = audioCtx.sampleRate; 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; noise.connect(audioCtx.destination); noise.start();

6. Aggiungere un effetto Delay (eco)

Simuliamo un’eco semplice con un DelayNode

// Oscillatore + delay + gain const osc4 = audioCtx.createOscillator(); const delay = audioCtx.createDelay(); const gain3 = audioCtx.createGain(); delay.delayTime.value = 0.25; // 250 ms gain3.gain.value = 0.4; osc4.connect(delay); delay.connect(audioCtx.destination); osc4.connect(audioCtx.destination); osc4.frequency.value = 440; osc4.start(); osc4.stop(audioCtx.currentTime + 1);

7. Visualizzare il suono (Analizzatore)

Possiamo “vedere” le onde sonore in tempo reale con il nodo AnalyserNode ed il metodo createAnalyser

let analyser, canvas, ctx, source, animationId; function startAnalyzer() { if (analyser) cancelAnimationFrame(animationId); analyser = audioCtx.createAnalyser(); const osc = audioCtx.createOscillator(); osc.type = 'square'; osc.frequency.value = 220; osc.connect(analyser); analyser.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + 2); canvas = document.getElementById('analyserCanvas'); ctx = canvas.getContext('2d'); const data = new Uint8Array(analyser.frequencyBinCount); function draw() { analyser.getByteTimeDomainData(data); ctx.clearRect(0,0,canvas.width,canvas.height); ctx.beginPath(); const slice = canvas.width / data.length; for (let i=0; i%lt;data.length; i++) { const x = i * slice; const y = (data[i] / 255) * canvas.height; i === 0 ? ctx.moveTo(x,y) : ctx.lineTo(x,y); } ctx.strokeStyle = '#69b7eb'; ctx.stroke(); animationId = requestAnimationFrame(draw); } draw(); }

8. Sintesi di un suono tipo “violino” 🎻

Usiamo più oscillatori con lievi variazioni di frequenza e un filtro passa‑basso per ottenere un timbro caldo.

// Violino semplice: più oscillatori + filtro + inviluppo di volume const attack = 0.3, release = 0.8; const baseFreq = 440; const filterV = audioCtx.createBiquadFilter(); filterV.type = 'lowpass'; filterV.frequency.value = 1200; const gainV = audioCtx.createGain(); gainV.gain.value = 0; filterV.connect(gainV); gainV.connect(audioCtx.destination); for (let i=0; i<3; i++) { const oscV = audioCtx.createOscillator(); oscV.type = 'sawtooth'; oscV.frequency.value = baseFreq + i*1.5; // leggere differenze oscV.connect(filterV); oscV.start(); oscV.stop(audioCtx.currentTime + attack + release); } // inviluppo: attacco e rilascio dolce const now = audioCtx.currentTime; gainV.gain.linearRampToValueAtTime(0.4, now + attack); gainV.gain.linearRampToValueAtTime(0, now + attack + release);

9. Parametri tipici per diversi strumenti sintetici

Ecco una panoramica di parametri comuni per riprodurre, per imitazione, alcuni strumenti. Puoi adattarli nei tuoi script per ottenere timbri convincenti.

Strumento Forma d’onda Filtro Attack Release Note / Timbro
Piano sine + sawtooth (mix) lowpass ~2 kHz 0.01 s 1.0 s Attacco rapido, decadimento medio, suono rotondo.
Violino sawtooth x3 detuned ±2 Hz lowpass ~1.2 kHz 0.3 s 0.8 s Tono caldo; inviluppo dolce e sostenuto.
Chitarra triangle o square bandpass 700 Hz–2 kHz 0.02 s 0.6 s Attacco deciso, corpo centrale, decadimento veloce.
Pad / Synth sawtooth + sine lowpass 800 Hz 1.0 s 2.5 s Tessitura morbida, ideale per tappeti sonori.
Kick (cassa) sine none 0 s 0.3 s Frequenza 60→30 Hz in discesa, inviluppo brevissimo.
Snare (rullante) white noise + sine (200 Hz) highpass ≥ 1 kHz 0 s 0.25 s Rumore secco con punta tonale.
Hi‑Hat (charleston) white noise highpass ≥ 5 kHz 0 s 0.1 s Rumore filtrato ad alte frequenze.

💡 Tip: puoi combinare più oscillatori e filtri per creare nuovi timbri: ad esempio miscelare un’onda sine e una sawtooth con inviluppi differenti per emulare strumenti ibridi.

10. Esempio: Timbro ibrido (sine + sawtooth)

In questo esempio combiniamo due forme d’onda diverse con inviluppi separati e un filtro passa‑basso per ottenere un suono più “ricco”.

// Creiamo un "timbro ibrido" mescolando due oscillatori function playHybrid() { const baseFreq = 440; // frequenza della nota (La4) const attack = 0.2, release = 1.0; // tempi inviluppo const now = audioCtx.currentTime; // Nodo di filtro (attenua alte frequenze) const filter = audioCtx.createBiquadFilter(); filter.type = 'lowpass'; filter.frequency.value = 1800; // Nodo di volume finale const gain = audioCtx.createGain(); gain.gain.value = 0; filter.connect(gain); gain.connect(audioCtx.destination); // --- Oscillatore 1: onda sinusoidale, suono morbido const osc1 = audioCtx.createOscillator(); osc1.type = 'sine'; osc1.frequency.value = baseFreq; osc1.connect(filter); osc1.start(); osc1.stop(now + attack + release); // --- Oscillatore 2: onda a dente di sega, aggiunge brillantezza const osc2 = audioCtx.createOscillator(); osc2.type = 'sawtooth'; osc2.frequency.value = baseFreq; osc2.connect(filter); osc2.start(); osc2.stop(now + attack + release); // --- Inviluppo di volume (attack + release) gain.gain.linearRampToValueAtTime(0.5, now + attack); // attacco gain.gain.linearRampToValueAtTime(0, now + attack + release); // rilascio }

11. Gestire il tempo (BPM)

La Web Audio API usa un clock interno ad alta precisione audioCtx.currentTime Possiamo usarlo per programmare eventi musicali in base a un BPM (battiti per minuto).

// Funzione per calcolare la durata di una battuta in secondi function getBeatDuration(bpm) { return 60 / bpm; } // Programma una semplice sequenza ritmica function playBeatSequence() { const bpm = 120; const beat = getBeatDuration(bpm); const now = audioCtx.currentTime; for (let i = 0; i < 8; i++) { const t = now + i * beat; const osc = audioCtx.createOscillator(); const gain = audioCtx.createGain(); osc.type = 'square'; osc.frequency.value = (i % 4 === 0) ? 220 : 440; // alterna due suoni gain.gain.value = 0.2; osc.connect(gain); gain.connect(audioCtx.destination); osc.start(t); osc.stop(t + 0.1); // ogni nota dura 100ms } }

💡 Puoi usare lo stesso principio per costruire un sequencer: programmare più strumenti (kick, snare, hihat) con diverse posizioni nella misura.

12. Note musicali e tastiera del computer 🎹

Ogni nota musicale può essere calcolata partendo dal La4 = 440 Hz. La formula tipica è:

frequency = 440 * Math.pow(2, (semitoni da A4) / 12)

Possiamo poi collegare i tasti della tastiera fisica a note MIDI per suonare piccoli frammenti.

// Mappa tasti → semitoni (rispetto ad A4) const keyMap = { 'a': 0, // La4 'w': 1, // La#4 's': 2, // Si4 'd': 3, // Do5 'r': 4, // Do#5 'f': 5, // Re5 't': 6, // Re#5 'g': 7, // Mi5 'h': 9, // Fa#5 'j': 11, // Sol#5 'k': 12 // La5 }; // Funzione per suonare una nota function playNote(semitone) { const freq = 440 * Math.pow(2, semitone / 12); const osc = audioCtx.createOscillator(); const gain = audioCtx.createGain(); osc.frequency.value = freq; osc.type = 'sine'; gain.gain.value = 0.3; osc.connect(gain); gain.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + 0.5); // durata breve } // Listener tastiera window.addEventListener('keydown', e => { const semitone = keyMap[e.key]; if (semitone !== undefined) { playNote(semitone); } });

✅ Prova a premere A S D F G H J K sulla tastiera del tuo computer: sentirai le note che si susseguono!

Puoi ampliare la mappa aggiungendo più tasti o creando modalità diverse (scale maggiori, minori, pentatoniche, ecc.).


Prova la nostra Drum Machine Javascript!
Prova il nostro Synth Javascript!