Ceci est un article technique sur la façon dont Morph's Studio de musique — une station de travail numérique audio complète incluant 5 instruments natifs, un Mixer avec des chaînes d'effets et des envois auxiliaires, 30+ effets (dont 5 ports Airwindows Tier-1 et un limiteur true-peak), une rack d'effets MIDI, un hôte de plugins WAM v2, et une couche de collaboration Yjs CRDT — fonctionne entièrement dans un onglet navigateur.
La question pratique est : est-ce que ceci vraiment est compétitif avec les DAW de bureau ? La réponse honnête : pour la plupart des workflows de producteur, oui. Pour les écosystèmes de périphériques tiers profonds, pas encore. Ouvrez Studio de musique pour entendre le produit lui-même. Ce post est l'ingénierie.
La pile
- Frontend React + Vite
- Web Audio API pour le graphique audio
- AudioWorkletProcessor pour DSP personnalisé (hors-thread principal)
- WebAssembly pour les traitements DSP critiques en performance (builds WASM personnalisés)
- MessagePort + ring buffers pour la communication UI ↔ worklet
- Yjs pour la collaboration CRDT
- IndexedDB pour la persistance des projets + stockage d'échantillons
- WebMIDI pour l'entrée des contrôleurs matériels
- OfflineAudioContext pour rebond/export
1. Le graphique audio
Chaque piste dans le Studio de musique suit une chaîne : instrument → chaîne d'effets de piste → envoi auxiliaire → bande de mixage → bus principal → limiteur → destination. Chaque flèche représente une connexion Web Audio AudioNode.
Web Audio nous donne AudioContext, GainNode, BiquadFilterNode, ConvolverNode, AnalyserNode et autres. Ces éléments sont optimisés par les moteurs de navigateur (V8/SpiderMonkey/JSC investis en profondeur). Pour les effets standards — EQ, automation de gain, filtrage de base — nous utilisons des nœuds natifs. Ils sont rapides et bien testés.
Pour des effets personnalisés — ports Airwindows, notre saturation, notre limiteur true-peak — nous passons à AudioWorkletProcessor, qui s'exécute sur le thread audio, avec une priorité temps réel garantie, et est l'emplacement architectural correct pour un DSP non standard.
class PurestDriveProcessor extends AudioWorkletProcessor {
process(inputs, outputs) {
const input = inputs[0];
const output = outputs[0];
// Airwindows PurestDrive port — ~30 LOC of saturation math
for (let ch = 0; ch < input.length; ch++) {
for (let i = 0; i < input[ch].length; i++) {
const s = input[ch][i];
// [...tape-saturation math...]
output[ch][i] = processed;
}
}
return true;
}
}
registerProcessor('purest-drive', PurestDriveProcessor);
Les Worklets sont livrés dans public/audio-worklets/*.js et sont chargés via audioContext.audioWorklet.addModule(path). Une fois chargés, n'importe quelle partie du graphique peut instancier un AudioWorkletNode pour ce processeur. Le traitement s'exécute hors du thread principal — les animations de l'interface utilisateur ne perturbent pas l'audio.
2. Les 5 ports Tier-1 Airwindows
Chris Johnson's Airwindows est 2 500+ plugins audio, sous licence MIT, légendairement savoureux en DSP. Nous avons porté 5 plugins vers AudioWorklets du navigateur pour Wave 12 du Studio de musique :
- Pressure6 — compression transparente qui respire
- ToTape9 — modélisation de saturation de bande analogique
- PurestDrive — saturation à phase minimale sans aigus durs
- CreamCoat — chaleur + excitateur harmonique
- NotJustAnotherDither — Dither TPDF pour réduction de profondeur de bits (maîtrise)
Chaque port est ~30-80 LOC de mathématiques dans un AudioWorkletProcessor. Les ports ne correspondent pas bit à bit à Airwindows (différents traitements FMA/dénormaux entre les moteurs numériques C++ et JS) mais correspondent soniquement. Les tests nuls maintiennent les sorties ≤ -80 dBFS résiduels par rapport aux références, donc les changements DSP sont détectés.
3. Le limiteur de crête réel
Les plateformes de streaming modernes (Spotify, Apple Music, YouTube) appliquent la normalisation du niveau sonore vrai-pic selon ITU-R BS.1770-5. Les pics d'échantillonnage et les pics inter-échantillonnage sont des choses différentes ; un signal peut atteindre -1 dBFS au niveau de l'échantillon mais dépasser 0 dBFS vrai-pic après la reconstruction D/A. Un limiteur non-vrai-pic clippera dans le DAC même si votre DAW l'affiche propre.
Notre limiteur implémente spécifiquement la norme ITU : 4x échantillonnage, détection des pics inter-échantillons, lookahead, relâchement à knee doux. Il est livré sous forme d'AudioWorklet à public/audio-worklets/true-peak-limiter-processor.js.
4. Communication hors-thread principale
Le graphique audio s'exécute sur le thread audio. L'interface utilisateur s'exécute sur le thread principal. Les modifications des paramètres (tourner un bouton, atténuer une piste) doivent se propager de l'interface utilisateur vers l'audio sans perturber le son.
Web Audio utilise nativement AudioParam pour les paramètres standards (GainNode gain, BiquadFilterNode frequency). Pour les travaux personnalisés avec worklet, nous utilisons MessagePort avec clonage structuré pour les messages UI, et des tampons circulaires basés sur SharedArrayBuffer pour les données d'automatisation à haut débit.
SharedArrayBuffer nécessite Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp dans les en-têtes. Ces en-têtes sont définis dans public/_headers pour les routes /soundbend + /pulse. Le Tape Delay utilise un tampon circulaire à 96 kHz pour éviter la latence des ports de message.
5. Les 5 instruments natifs
Le Studio de musique embarque 5 instruments, tous personnalisés :
Synthèse FM — Synthèse FM à 6 opérateurs modélisée sur DX7 / Operator. Visualiseur d'algorithme + matrice de modulation complète. ~800 LOC JS pour l'interface, ~300 LOC pour le worklet de gestion des voix.
Synthé wavetable — synthèse morph-between-tables. Déformation spectrale + unison + détune stéréo. 30+ tables de formes d'onde par défaut. Utilise ConvolverNode pour le fondu croisé de wavetable à vitesse audio.
Analog Subtractive — polysynthé avec 2 oscillateurs et filtres Cytomic SVF. Cytomic est la topologie de filtre à variable d'état qui sonne juste ; une SVF naive présente des problèmes d'aliasing au-delà de fc > SR/6. La portée Cytomic gère cela via des mathématiques de rétroaction à retard zéro.
DrumRack + Sampler — lecture d'échantillons par pads avec enveloppes de hauteur, filtre et ampli par pad. Les échantillons sont stockés dans IndexedDB pour un rappel rapide. 5 kits d'usine spécifiques aux genres (boombap, trap, house, dnb, afrobeat).
SeedSynth + Geist (dans le moteur AudioStudioEngine hérité — désormais intégré au Studio de musique depuis la Wave 14) — un générateur de patch génétique (Synplant 2-class) et une machine à batterie de style Geist. Ce sont les brevets propriétaires qui ont rendu la doctrine de consolidation importante — nous avons dû préserver un comportement identique à l'octet après une fusion de 30 000 lignes. Test null de 100 graines réussi après la fusion.
6. Le Mixer + envois auxiliaires
Un véritable Mixer — non pas seulement des faders par piste mais des inserts d'effets par piste, des envois auxiliaires pré/post-fader, bus master — est ce qui distingue un DAW d'un séquenceur. Le Mixer du Studio de musique est classé ~655 LOC (src/audio/mixer.js). Chaque piste est un graphe AudioNode avec des emplacements d'insertion, des points de prise pour les envois et un compteur.
La partie délicate est la différence entre les envois auxiliaires pré-fader et post-fader. Les envois pré-fader vont vers un bus de réverbération indépendamment du volume de la piste (utilisation classique : réverbération parallèle qui survit à une atténuation de piste). Les envois post-fader évoluent avec le volume de la piste. Les deux sont disponibles.
7. Hôte de plugins MIDI + WAM
WebMIDI est mature — les entrées de contrôle à partir de n'importe quel clavier MIDI USB fonctionnent parfaitement. Notre rack MIDI (Arpeggiator, Scale, Chord) se situe entre le contrôleur et l'instrument, transformant les flux de notes.
Pour les plugins, nous hébergeons Web Audio Modules v2 (WAM) — des plugins natifs au navigateur. Écosystème plus petit que VST (avance de 25 ans) mais en croissance. Charger un WAM se fait avec getWamHost().load(wamUri), ce qui renvoie un AudioNode que vous pouvez insérer dans n'importe quelle chaîne.
Les WAM nécessitent un CSP par route car ils chargent du JS fourni par l'utilisateur arbitraire. Nos _headers limitent étroitement le CSP pour /soundbend avec une liste autorisée script-src pour les registres WAM bien connus.
8. OfflineAudioContext pour rebondir
L'exportation s'effectue via OfflineAudioContext — rendu plus rapide que temps réel (ou plus lent, selon la complexité de la chaîne) vers un AudioBuffer statique, puis encodage en WAV via notre encodeur intégré.
Attention : tous les nœuds ne fonctionnent pas dans OfflineAudioContext. MediaStreamAudioSourceNode ne fonctionne pas (impossible d'enregistrer un micro en direct hors ligne, évidemment). Certains worklets nécessitent une réinitialisation soigneuse de l'état entre les transitions en ligne → hors ligne. Nous avons rencontré ce problème à plusieurs reprises et maintenant Mixer.toJSON() + Mixer.fromJSON(offlineCtx) est le modèle canonique : sérialisez l'état du mixer, reconstruisez un mixer parallèle dans le contexte hors ligne, puis effectuez le rendu.
9. Collaboration via Yjs
Yjs est une bibliothèque CRDT qui gère l'édition collaborative multi-utilisateurs avec résolution automatique des conflits. Notre classe SoundbendSession enveloppe Yjs avec une structure de document par piste (Y.Map pour les métadonnées de piste, Y.Array pour les étapes de motif, Y.Text pour les paroles/remarques).
Deux à quatre producteurs peuvent éditer le même projet simultanément avec une synchronisation précise au niveau des échantillons. Cela est plus difficile que la collaboration sur des documents texte car les données audio sont d'un ordre de grandeur supérieur — nous partageons les métadonnées et les schémas via Yjs, pas les données brutes. Les échantillons bruts restent locaux (IndexedDB par utilisateur).
10. Budget de performance
Exécuter une DAW complète dans un navigateur avec une interface utilisateur à 60 fps + audio en temps réel + SharedArrayBuffer + WASM + plusieurs worklets nécessite des budgets serrés. Quelques chiffres :
- Fil audio : ~2 ms / tampon de 128 échantillons à 48 kHz (sous-trame)
- Thread principal : 16,6ms / 60fps par image ; la gestion audio doit rester <1ms par image
- Configuration du Worklet : coût unique de ~10-50ms par chargement de worklet (livrer 15+ worklets — nous regroupons)
- Allocation de SharedArrayBuffer : tampons circulaires de 4 Ko par paramètre de worklet dynamique
- Taille du chunk Studio de musique (après consolidation) : ~496 Ko bruts, ~120 Ko gzip
- Chargement complet du projet (froid) : ~800 ms sur un ordinateur portable moyen
- Export render : ~0,3x à 2x temps réel selon la complexité de la chaîne d'effets
11. Ensemble de tests null
Les régressions DSP sont silencieuses — l'audio sonne différemment de manières subtiles que la revue de code ne peut détecter. Le Studio de musique est livré avec un ensemble de tests piloté par Puppeteer dans tests/audio-null/.
Pour chaque effet × préréglage × signal de test, le système de test génère via OfflineAudioContext dans un Chromium headless, teste null le résultat WAV contre un golden commité, et échoue la construction si le pic résiduel dépasse -60 dBFS.
Wave 14F a ajouté 100 bases de graines SeedSynth et 32 motifs de batterie Geist dorés. Lorsque nous avons fusionné Studio de musique dans AudioStudio (Wave 14B-G), le test null a détecté tout ce qui aurait pu briser silencieusement les instruments propriétaires. 96 des 100 graines SeedSynth ont passé le test bit-identique. Les 4 échecs étaient dus à des défauts préexistants dans Chromium Web Audio (drift de résonance du filtre à haute qualité entre les instances OfflineAudioContext) — pas des régressions de fusion, et non corrigibles depuis JavaScript.
Les régressions DSP sont silencieuses. La revue de code ne peut pas les entendre. Le dispositif de test null le peut — c'est pourquoi il bloque la CI.
12. Pourquoi le navigateur + client-side était le bon choix
Les DAW natives ont un avantage de 30 ans. Battre leurs fonctionnalités une par une est une longue route. Mais prix — cela a été gagné immédiatement en faisant des choix architecturaux différents.
WebAssembly a franchi la barrière des performances entre 2022 et 2024. Les Web Audio AudioWorklets ont été largement disponibles autour de la même période. SharedArrayBuffer + COOP/COEP a stabilisé. Chromium, Firefox et Safari prennent tous en charge les API nécessaires avec des performances adéquates.
L'implication tarifaire : si le traitement audio se fait sur l'appareil de l'utilisateur, nous ne payons pas pour cela. Notre coût marginal par abonné Pro est presque nul.
Lisez le manifeste pour l'économie. Pour le DSP, c'est la plupart de l'histoire.
Essayez-le
Le Studio de musique est à morph.cool/music-studio. La version gratuite fonctionne sans inscription. Clavier WebMIDI bienvenu, mais non requis.
— l'équipe Morph
22 avril 2026