Web Audio API di JavaScript
Scopri la Web Audio API di JavaScript: crea un grafo audio con AudioContext e AudioNode, carica e riproduci suoni, controlla il volume e genera toni.
La Web Audio API ti permette di generare, caricare, elaborare e riprodurre suoni direttamente nel browser, senza bisogno di plugin. Questa guida ti accompagna attraverso i suoi elementi fondamentali: come creare un contesto audio, caricare e riprodurre file sonori, controllare il volume, generare toni con un oscillatore e aggirare il criterio di riproduzione automatica (autoplay policy) del browser. Ogni esempio è breve e autonomo, così puoi inserirlo in una pagina e sentire subito il risultato.
Introduzione alla Web Audio API
La Web Audio API è una API JavaScript per elaborare e sintetizzare audio nelle applicazioni web. A differenza di un semplice elemento <audio>, che si limita a riprodurre un file dall'inizio alla fine, la Web Audio API ti mette a disposizione un grafo di nodi collegati tra loro che instradano, mescolano e trasformano il suono in tempo reale. È quindi lo strumento giusto per giochi, app musicali, sintetizzatori e visualizzatori audio.
Pensare in termini di grafi audio
Ogni suono nella Web Audio API attraversa una catena di nodi, da una sorgente a una destinazione:
source → (effects) → destination
(file, (gain, (your
oscillator) filter) speakers)I tre concetti che incontrerai per primi:
- AudioContext: il contenitore che possiede l'intero grafo e il clock audio. Ne crei uno per pagina e lo riutilizzi.
- AudioNode: un'unità modulare di elaborazione. I nodi sorgente producono suono (un buffer o un oscillatore), i nodi effetto lo trasformano (gain, filtro, panner) e il nodo destination lo invia agli altoparlanti.
- AudioBuffer: dati audio decodificati e residenti in memoria, ideali per suoni brevi (effetti, campioni) che attivi più volte con tempi precisi.
Costruisci un grafo chiamando node.connect(otherNode). L'audio termina sempre in audioContext.destination.
Primi passi con la Web Audio API
Per fare qualsiasi cosa con l'audio, devi prima creare un AudioContext, che funge da contenitore del tuo grafo audio.
Creare un AudioContext
const audioContext = new (window.AudioContext || window.webkitAudioContext)();Questo inizializza un nuovo AudioContext. Il fallback webkit copre le versioni più vecchie di Safari; i browser moderni supportano direttamente AudioContext senza prefisso.
Il tranello della autoplay policy. I browser creano il contesto nello stato
"suspended"e si rifiutano di riprodurre suoni finché l'utente non interagisce con la pagina (un clic o la pressione di un tasto). Riattivalo sempre all'interno di un gestore di eventi prima di riprodurre:button.addEventListener("click", async () => { if (audioContext.state === "suspended") { await audioContext.resume(); } // ...now it is safe to play });Se l'audio è muto e nella console non compaiono errori, la causa più comune è un contesto non riattivato.
Caricare e riprodurre l'audio
Per riprodurre un file sonoro, lo carichi in un AudioBuffer e poi colleghi un nodo sorgente al contesto per la riproduzione.
Caricare i file audio
Questo codice usa la Fetch API e async/await per scaricare il file e decodificarlo:
async function loadAudioFile(url, audioContext) {
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
return audioBuffer;
}La funzione scarica il file, lo legge come ArrayBuffer grezzo e lo passa a decodeAudioData, che decomprime formati come MP3, WAV o OGG in un AudioBuffer pronto per la riproduzione. La decodifica avviene fuori dal thread principale e restituisce una promise, quindi usa await.
Riprodurre l'audio caricato
function playAudio(audioBuffer, audioContext) {
const sourceNode = audioContext.createBufferSource();
sourceNode.buffer = audioBuffer;
sourceNode.connect(audioContext.destination);
sourceNode.start();
}Questo codice crea un BufferSourceNode, lo punta al buffer decodificato, lo collega agli altoparlanti (audioContext.destination) e avvia la riproduzione.
Un nodo sorgente è monouso. Un
AudioBufferSourceNodepuò essere avviato una sola volta: dopo che si è fermato, devi crearne uno nuovo per riprodurre di nuovo lo stesso buffer. L'AudioBufferin sé è riutilizzabile, quindi conserva il buffer e ricrea ogni volta il nodo sorgente, che è leggero:// ❌ Throws after the first play: "cannot call start more than once" // sourceNode.start(); // sourceNode.start(); // ✅ One fresh source node per playback function playOnce(buffer, ctx) { const src = ctx.createBufferSource(); src.buffer = buffer; src.connect(ctx.destination); src.start(); }
Manipolare l'audio
La Web Audio API dà il meglio di sé quando inserisci nodi effetto tra la sorgente e la destinazione. Il più comune è il gain node per il controllo del volume.
Creare un Gain Node
function createGainNode(audioContext, volume) {
const gainNode = audioContext.createGain();
gainNode.gain.value = volume; // 1 = full volume, 0 = silent, 0.5 = half
return gainNode;
}Un GainNode moltiplica il segnale per il valore di gain. La proprietà gain è un AudioParam, il che significa che puoi anche programmare variazioni graduali nel tempo (per le dissolvenze) invece di impostare un valore in modo brusco:
// Fade from silent to full volume over 2 seconds, avoiding clicks/pops
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + 2);Collegare i nodi per gli effetti audio
function applyEffects(audioBuffer, volume, audioContext) {
const sourceNode = audioContext.createBufferSource();
sourceNode.buffer = audioBuffer;
const gainNode = createGainNode(audioContext, volume);
// source → gain → destination
sourceNode.connect(gainNode);
gainNode.connect(audioContext.destination);
sourceNode.start();
}Questo costruisce un grafo di tre nodi: la sorgente alimenta il gain node e il gain node alimenta la destinazione. Puoi inserire altri nodi effetto allo stesso modo, ad esempio un BiquadFilterNode per modellare il timbro:
const filter = audioContext.createBiquadFilter();
filter.type = "lowpass"; // let low frequencies through, cut highs
filter.frequency.value = 800; // cutoff in Hz
sourceNode.connect(filter);
filter.connect(gainNode);
gainNode.connect(audioContext.destination);L'ordine conta: il segnale scorre nella direzione delle tue chiamate a connect.
Un esempio eseguibile: un generatore di toni
Per una demo autonoma che non richiede alcun file audio, ecco un generatore di toni costruito con un oscillatore. Gestisce anche la autoplay policy riattivando il contesto al primo clic.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Simple Tone Generator</title>
</head>
<body>
<h1>Simple Tone Generator</h1>
<button onclick="playTone()">Play Tone</button>
<button onclick="stopTone()">Stop Tone</button>
<script>
let audioContext;
let oscillator;
async function playTone() {
if (!audioContext) {
audioContext = new (window.AudioContext ||
window.webkitAudioContext)();
}
// Satisfy the autoplay policy: resume on user gesture
if (audioContext.state === "suspended") {
await audioContext.resume();
}
if (!oscillator) {
oscillator = audioContext.createOscillator();
oscillator.type = "sine"; // Sine wave — other values are 'square', 'sawtooth', 'triangle'
oscillator.frequency.setValueAtTime(440, audioContext.currentTime); // A4 note, 440 Hz
oscillator.connect(audioContext.destination);
oscillator.start();
}
}
function stopTone() {
if (oscillator) {
oscillator.stop();
oscillator.disconnect();
oscillator = null; // Reset the oscillator to allow a new one to be created next time
}
}
</script>
</body>
</html>Come funziona:
- Nodo oscillatore: genera un'onda sinusoidale pura a 440 Hz (la nota La4 usata per l'accordatura). Gli altri valori di
typesono"square","sawtooth"e"triangle", ciascuno con un timbro diverso. - Funzioni di controllo:
playToneriattiva il contesto e avvia l'oscillatore;stopTonelo ferma, lo scollega e reimposta la variabile, in modo che al prossimo avvio si possa creare un nuovo oscillatore. - Perché reimpostare
oscillator = null? Come un buffer source, un oscillatore è monouso: una volta fermato non può essere riavviato, quindi ne costruisci uno nuovo per il tono successivo.
Quando usare la Web Audio API
| Caso d'uso | Cosa scegliere… |
|---|---|
| Riprodurre una singola traccia di sottofondo o un podcast | Un semplice elemento <audio>: più semplice, meno codice |
| Effetti sonori in un gioco con tempi precisi | Web Audio API + AudioBuffer |
| Controllo del volume, dissolvenze, filtri, panning | Nodi gain/filter/panner della Web Audio API |
| Sintetizzatori e toni generati | OscillatorNode della Web Audio API |
| Visualizzatori di forma d'onda/frequenza in tempo reale | AnalyserNode della Web Audio API |
Se ti serve solo avviare e fermare la riproduzione, l'elemento <audio> è sufficiente. Scegli la Web Audio API nel momento in cui hai bisogno di elaborazione, temporizzazione o sintesi.
Conclusione
La Web Audio API trasforma il browser in una console di missaggio programmabile: colleghi un grafo di nodi da una sorgente, attraverso gli effetti, fino agli altoparlanti, e controlli ogni passaggio in tempo reale. Tieni a mente questi punti chiave:
- Crea un solo
AudioContexte riattivalo con un gesto dell'utente prima di riprodurre. - Riutilizza gli
AudioBuffer, ma crea un nuovo nodo sorgente/oscillatore per ogni riproduzione. - Costruisci gli effetti concatenando i nodi con
connect(), terminando sempre inaudioContext.destination.
Da qui puoi esplorare altre funzionalità del browser correlate, come la Web MIDI API per collegare strumenti musicali, oppure la Web Animations API per sincronizzare gli elementi visivi con l'audio.