Data Coding
Web Audio API
2025 - 2026
C'est quoi ?
Cette API permet de contrôler des données audio: jouer, modifer, créer ou analyser des sons.
Concepts
Notre outil principal est un contexte audio, on va pouvoir créer des nœuds (node) que nous lierons ensemble pour créer notre application.
Un nœud peut servir à gerer le volume, tandis qu'un autre s'occuperait d'ajouter un filtre.
Play, stop, pause, resume
Contexte audio
La première chose à faire est de mettre en place notre contexte. Il contiend tous les outils nécessaire pour la suite:
// On crée un nouveau contexte audio
const audioContext = new AudioContext();
Charger un fichier audio
// on crée une fonction avec 2 arguments contenant
// l'url vers le fichier audio, et notre contexte.
const loadSample = async ( audioFilePath, audiocontext ) => {
// on récupère notre fichier
const response = await fetch( audioFilePath );
// on transforme ce fichier en un tableau de données
// qu'on décode pour le rendre lisible par l'api.
const arraybuffer = await response.arrayBuffer();
audiobuffer = await audiocontext.decodeAudioData( arraybuffer );
return audiobuffer;
};
Les nœuds
Il va falloir lier différents nœuds pour jouer notre son, au plus simple on aura un nœud d'entrée (=source) qu'il faudra connecté à la sortie (=destination).
Pour simplifier, la source c'est un fichier audio + lecteur, et la destination ce sont vos écouteurs.
// On crée le nœud source, qui contiendra notre son.
let source = audioContext.createBufferSource();
// On passe le son dans la source
source.buffer = audioBuffer;
// On le connecte à la destination du contexte.
source.connect( audioContext.destination );
Le Web Audio API considère qu'une source ne doit être jouée qu'une seule fois. À chaque fois qu'on voudra relire un son, il faudra redéfinir notre source pour pouvoir la jouer.
On va donc englober notre dernier bout de code dans une fonction qui permettra de s'en occuper autant de fois que nécessaire:
const audioBuffer = await laodSample( 'path/to/sound.mp3', audioContext );
let source;
const setSource = () => {
source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect( audioContext.destination );
};
Play
Pour jouer un son, il faut prévoir une fonction qui doit être lié à un évènement. On ne peut pas lancer automatiquement un son via l'API, effectivement il y a une protection par défaut qui évite de jouer des sons sans une action de l'utilisateur.
const play = () => {
// Défini ou redéfini notre source
setSource();
// Joue notre source
source.start();
};
// On rajoute un click event sur un élement
// qui lancera notre fonction play
element.addEventListener( 'click', play );
Propriété onended
Pour gérer nos contrôles, il serait bon de savoir quand un son à fini d'être joué. On va ajouter une fonction sur la propriété onended de notre source qu'on rajoute dans notre fonction setSource :
const setSource = () => {
source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect( audioContext.destination );
// On rajoute une fonction sur la propirété onended
source.onended = () => {
// Notre logique de fin ici
console.log( 'ended' );
}
};
Stop
const stop = () => {
source.stop();
};
Pause
const pause = () => {
const state = options.audioCtx.state;
if( state === "running" ){
options.audioCtx.suspend().then( () => {
options.btnPause.textContent = "Resume";
} );
} else if( state === "suspended" ){
options.audioCtx.resume().then( () => {
options.btnPause.textContent = "Pause";
} );
}
};
Less code, more sound
Analyser le son
Nœud d'analyse
Pour récupérer les données de notre son, il faut rajouter un nœud d'analyse à notre code:
const analyser = audioContext.createAnalyser();
Domaine fréquentiel
Le domaine fréquentiel correspond (grossièrement) au nombre de fréquences accessible par notre analyseur.
La taille ne peut être qu'une puissance de 2 avec pour minimum 32 et pour maximum 32 768. Par défaut sa valeur est de 2048.
const analyser = audioContext.createAnalyser();
analyser.fftSize = 64;
Récupération des données
Il est possible de récupérer les données de 2 manières différentes, avec 2 niveaux de précisions et de performances.
Performances
- getByteFrequencyData( )
- getByteTimeDomainData( )
Précision
- getFloatFrequencyData( )
- getFloatTimeDomainData( )
Préparer les données
Avant de récupérer les données, il nous faut créer un tableau vide pour les contenir. Partons sur l'option performance.
Comme nous allons avoir à faire à des nombres entre 0 et 255, Uint8Array est tout indiqué.
const analyser = audioContext.createAnalyser();
analyser.fftsize = 64;
// On crée un tableau qui contiendra des valeurs entre 0 et 255.
// 'length' représente le nombre d'éléments dans notre tableau
const datas = new Uint8Array( length );
Avec getFloatFrequencyData( ), nous aurions utilisé Float32Array à la place de Uint8Array.
La longueur de notre tableau sera définie par la taille de notre domaine fréquentiel via frequencyBinCount. Pour des raisons techniques, cette taille sera toujours la moitié du fftSize.
const analyser = audioContext.createAnalyser();
analyser.fftsize = 64;
// On crée un tableau qui contiendra des valeurs entre 0 et 255.
// 'frequencyBinCount' définira la longueur de notre jeu de données.
const datas = new Uint8Array( analyser.frequencyBinCount );
Remplir notre tableau de données
const datas = new Uint8Array( analyser.frequencyBinCount );
// Cette méthode prend comme argument le tableau
// que nous avons créé au préalable.
// Elle remplira le tableau de données via la source
// une fois cette dernière connectée.
analyser.getByteFrequencyData( datas );
Il faudra appeler continuellement cette fonction pour animer sur le son.