This is a technical post about how Morph's Soundbend — a full digital audio workstation including 5 native instruments, a Mixer with FX chains and aux sends, 30+ effects (including 5 Airwindows Tier-1 ports and a true-peak limiter), a MIDI effects rack, a WAM v2 plugin host, and a Yjs CRDT collaboration layer — runs entirely in a browser tab.
Het praktische vraagstuk is: is dit echt concurrerend met Ableton? Het eerlijke antwoord: voor de meeste producentenwerkstromen, ja. Voor diepe Max for Live integraties, nog niet. Lees de Ableton alternatief vergelijking voor een productniveau vergelijking. Dit bericht is de engineering.
De stack
- React + Vite frontend
- Web Audio API voor het audioverwerkingsnetwerk
- AudioWorkletProcessor voor aangepaste DSP (off-main-thread)
- WebAssembly voor prestatiekritische DSP (aangepaste WASM-bouwen)
- MessagePort + ring buffers voor UI ↔ worklet-communicatie
- Yjs voor CRDT-samenwerking
- IndexedDB voor projectopslag + voorbeeldopslag
- WebMIDI voor hardwarecontroller invoer
- OfflineAudioContext voor bounce/export
1. Het audioverloop
Elke track in Soundbend gaat door een keten: instrument → track FX keten → aux send → Mixer strip → master bus → limiter → bestemming. Elke pijl is een Web Audio AudioNode-verbinding.
Web Audio geeft ons AudioContext, GainNode, BiquadFilterNode, ConvolverNode, AnalyserNode en vrienden. Deze zijn geoptimaliseerd door browsermotoren (V8/SpiderMonkey/JSC zijn diep betrokken). Voor standaard effecten — EQ, gain automatisering, basisfiltering — gebruiken we native nodes. Ze zijn snel en goed getest.
Voor custom effecten — Airwindows ports, onze saturatie, onze true-peak limiter — gaan we naar AudioWorkletProcessor, dat op de audio-thread draait, gegarandeerd real-time prioriteit heeft, en de juiste architecturale plaats is voor niet-standaard DSP.
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);
Worklets worden geleverd in public/audio-worklets/*.js en laden via audioContext.audioWorklet.addModule(path). Zodra geladen, kan elk deel van het grafiek een AudioWorkletNode instantiëren voor die processor. Verwerking vindt plaats op een aparte thread — UI-animaties verstoren geen audio.
2. De 5 Airwindows Tier-1 ports
Chris Johnson's Airwindows is 2.500+ audio plugins, MIT-licensed, legendarisch smaakvolle DSP. We hebben 5 geport naar browser AudioWorklets voor Soundbend's Wave 12 ship:
- Pressure6 — transparante compressie die ademt
- ToTape9 — analoge bandverzadiging modellering
- PurestDrive — minimale faseverzadiging zonder scherpe hoge tonen
- CreamCoat — warmte + harmonische exciter
- NotJustAnotherDither — TPDF dither voor bit-depth verlaging (mastering)
Elke poort is ~30-80 LOC wiskunde in een AudioWorkletProcessor. De poorten komen niet bit-per-bit overeen met Airwindows (verschillende FMA/denormal behandeling tussen C++ en JS numerieke engines), maar ze klinken hetzelfde. Null-tests houden de uitvoer ≤ -80 dBFS residu tegen goldens, dus DSP-wijzigingen worden opgemerkt.
3. De echte pieklimiter
Moderne streamingplatforms (Spotify, Apple Music, YouTube) eisen echte piek luidheid normalisatie volgens ITU-R BS.1770-5. Steekproefpieken en inter-voorbeeldpieken zijn verschillende dingen; een signaal kan pieken op -1 dBFS steekproefmatig maar hoger uitkomen dan 0 dBFS echte piek na D/A reconstructie. Een niet-echte piek limiter zal knippen in de DAC, ook al toont je DAW het schoon.
Onze limiter implementeert de volledige ITU-spec: 4x oversampling, inter-sample-peak detectie, lookahead, soft-knee release. Het wordt geleverd in AudioWorklet-vorm op public/audio-worklets/true-peak-limiter-processor.js. Metingen tegen referentie-implementaties (Oxford Limiter, iZotope Ozone Maximizer) komen binnen 0,1 dB op LUFS geïntegreerd overeen.
4. Communicatie buiten de hoofdthread
De audio-grafiek wordt uitgevoerd op de audio-thread. De UI wordt uitgevoerd op de hoofd-thread. Parameterwijzigingen (draai een knop, fade een track) moeten van UI → audio propageren zonder het audio te verstoringen.
Web Audio's AudioParam verwerkt dit nagenoeg standaard voor standaardparameters (GainNode gain, BiquadFilterNode frequency). Voor aangepaste worklet-parameters gebruiken we MessagePort met gestructureerd klonen voor UI-berichten, en SharedArrayBuffer-gebaseerde ringbuffertjes voor hoge snelheid automatiseringsgegevens.
SharedArrayBuffer vereist Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp headers. Deze zijn ingesteld in public/_headers voor de /soundbend + /pulse routes. De Tape Delay gebruikt een ringbuffer bij 96kHz om berichtpoortlatentie te voorkomen.
5. De 5 native instrumenten
Soundbend bevat 5 instrumenten, allemaal op maat:
FM Synth — 6-operator FM-synthese gemodelleerd op DX7 / Operator. Algoritmevisualisator + volledige modulatiematrix. ~800 LOC JS voor de UI, ~300 LOC worklet voor de stemallocator.
Wavetable Synth — morph-between-tables synthese. Spectrale vervorming + unisono + stereo detune. 30+ fabriekswavetables. Gebruikt ConvolverNode voor wavetable crossfade op audiorate.
Analog Subtractive — 2-oscillator polysynth met Cytomic SVF-filters. Cytomic is de state-variable filter topologie die goed klinkt; naive SVF heeft aliasing problemen boven fc > SR/6. De Cytomic port lost dit op via zero-delay feedback wiskunde.
DrumRack + Sampler — pad-gebaseerde sample afspelen met per-pad pitch/filter/amp envelopes. Samples worden opgeslagen in IndexedDB voor snelle herinnering. 5 genre-specifieke fabriekskits (boombap, trap, house, dnb, afrobeat).
SeedSynth + Geist (in de verouderde AudioStudioEngine — nu samengevoegd in Soundbend sinds Wave 14) — een genetische patch-generator (Synplant 2-class) en een Geist-stijl drummachine. Dit zijn de eigen merken die de consolidatie doctrine een groot succes maakten — we moesten bit-identieke gedrag behouden door een 30k-lijn samenvoeging. 100-zaad null-test geslaagd na samenvoeging.
6. De Mixer + aux-zenders
Een echte Mixer — niet alleen per-track faders maar ook FX inserts per track, pre/post-fader aux sends, master bus — is wat een DAW van een sequencer onderscheidt. De Mixer-klasse van Soundbend is ~655 LOC (src/audio/mixer.js). Elke track is een AudioNode-grafiek met insert slots, send tap punten en metering.
Het lastige deel is pre-fader versus post-fader aux-send. Pre-fader stuurt naar een reverbbus ongeacht de volume van de track (klassieke gebruik: parallelle reverb die overleeft een trackfade). Post-fader schaalt met trackvolume. Beide zijn standaard.
7. MIDI + WAM plugin host
WebMIDI is rijp — controllerinput vanaf elke USB-MIDI-toetsenbord werkt direct. Onze MIDI-rij (Arpeggiator, Schaal, Akkoord) zit tussen de controller en het instrument, en transformeert notenstromen.
Voor plugins hosten we Web Audio Modules v2 (WAM) — browser-native plugins. Kleinere ecosystem dan VST (25-jarige voorsprong) maar groeiend. Het laden van een WAM is getWamHost().load(wamUri) wat een AudioNode retourneert dat je kunt invoegen in elke keten.
WAMs vereisen per-route CSP omdat ze willekeurige, door de gebruiker geleverde JS laden. Onze _headers beperkt CSP nauwkeurig voor /soundbend met een script-src toegelijst voor bekende WAM-registeradressen.
8. OfflineAudioContext voor bounce
Export werkt via OfflineAudioContext — render sneller dan realtime (of langzamer, afhankelijk van de ketencomplexiteit) naar een statisch AudioBuffer, en encodeer vervolgens naar WAV met onze in-browser encoder.
Vang: niet alle knooppunten werken in OfflineAudioContext. MediaStreamAudioSourceNode werkt niet (kan geen live microfoon offline opnemen, uiteraard). Bepaalde worklets hebben een zorgvuldige state-reset nodig tussen online → offline overgangen. We hebben dit herhaaldelijk tegen gekomen en nu is Mixer.toJSON() + Mixer.fromJSON(offlineCtx) het canonieke patroon: serialiseer de mixerstatus, bouw een parallelle mixer op de offline context op, render.
9. Samenwerken via Yjs
Yjs is een CRDT-bibliotheek die meertalige samenwerkende bewerkingen met automatische conflictoplossing beheert. Onze SoundbendSession-klasse omvat Yjs met per-track documentstructuur (Y.Map voor track-metadata, Y.Array voor patroonstappen, Y.Text voor teksten/klanken).
Twee tot vier producenten kunnen tegelijkertijd hetzelfde project bewerken met sample-accurate track synchronisatie. Dit is moeilijker dan tekstdocumenten samenwerken omdat audiodata orders van grootte groter is — we delen metadata en patronen via Yjs, niet de ruwe audio. Ruwe samples blijven lokaal (IndexedDB per gebruiker).
10. Het prestatiebudget
Een volledige DAW in een browser uitvoeren met 60fps UI + realtime audio + SharedArrayBuffer + WASM + meerdere worklets vereist strakke budgetten. Enkele cijfers:
- Audio-thread: ~2ms / 128-sample buffer bij 48kHz (sub-frame)
- Hoofdthread: 16,6ms / 60fps kader; audioverwerking moet <1ms per kader blijven
- Worklet setup: one-time cost ~10-50ms per worklet load (ship 15+ worklets — we batch)
- SharedArrayBuffer toewijzing: 4KB ring buffers per dynamische worklet parameter
- Soundbend-bloksgrootte (na consolidatie): ~496 KB ruw, ~120 KB gzip
- Volledige projectlaad (koud): ~800ms op een middelmatige laptop
- Export render: ~0,3x tot 2x realtime afhankelijk van de complexiteit van de effectketen
11. Null-test harness
DSP regressies zijn stil — audio klinkt op subtiele manieren anders die code review niet kan vaststellen. Soundbend wordt geleverd met een Puppeteer-geleide null-test harness in tests/audio-null/.
Voor elke effect × preset × testsignaal renderen de harness via OfflineAudioContext binnen een headless Chromium, null-test het resulterende WAV tegen een gecommit golden, en mislukt de build als de residuele piek -60 dBFS overschrijdt.
Wave 14F heeft 100 SeedSynth seed baselines en 32 Geist drum-pattern goldens toegevoegd. Toen we Soundbend in AudioStudio (Wave 14B-G) integreerden, ving de null-test alles op wat mogelijk onzichtbaar zou zijn gebroken. 96 van de 100 SeedSynth seeds zijn bit-identiek geslaagd. De 4 mislukkingen waren reeds bestaande Chromium Web Audio-quirks (hoge-Q filter resonantie drift tussen OfflineAudioContext instanties) — geen merge regressies, en niet te herstellen vanuit JS.
DSP regressies zijn stil. Code review kan ze niet horen. De null-test harness kan dat — daarom is het CI-blokkerend.
12. Waarom browser + client-side de juiste keuze was
Native DAW's (Ableton, Logic, FL Studio) hebben een voorsprong van 30 jaar. Het verslaan ervan functie voor functie is een lange weg. Maar prijsstelling — dat was direct te winnen door andere architecturale keuzes te maken.
WebAssembly heeft in 2022-2024 de prestatiebarrière overschreden. Web Audio AudioWorklets zijn rond dezelfde tijd wijdverspreid uitgebracht. SharedArrayBuffer + COOP/COEP is gestabiliseerd. Chromium, Firefox en Safari ondersteunen allemaal de nodige API's met voldoende prestaties.
De prijsimplicatie: als audioverwerking op het apparaat van de gebruiker plaatsvindt, betalen wij er niet voor. Onze marginale kosten per Pro-abonnee zijn bijna nul. Dus $4.99/mo is duurzaam voor altijd. Een kind in Lagos met een Android van 2020 krijgt een volledige DAW die iemand in San Francisco eveneens $4.99 betaalt om te gebruiken.
Lees de prijsarchitectuur essay voor de economie. Voor de DSP is dat het grootste deel van het verhaal.
Probeer het
Soundbend is te vinden op morph.cool/soundbend. De gratis tier werkt zonder registratie. $4,99/maand Pro activeert de mastering chain + true-peak limiter + extra voorinstellingen. WebMIDI toetsenbord welkom, niet vereist.
— Hayden + het Morph agent team
22 april 2026