Made by Seven Systems ☰

A DAW at 5% CPU, using nothing but stock WebAudio nodes

No AudioWorklet, custom DSP, or WebAssembly. No dependencies, package manager, or build pipeline either (beyond a CoffeeScript compiler). A heavy production runs at around 5% CPU on a cheap laptop.

This page explains how it is done, partly because it is interesting and partly because anyone thinking about maintaining this codebase would want to know.

~5% average CPU, 26 synth instances and 66 insert FX
~250 MB RAM for that same production, including samples and UI
0 lines of custom DSP
0 third-party dependencies

Only standard nodes

The conventional route to a serious browser audio application is AudioWorklet: DSP in JavaScript or WebAssembly, run on the audio thread. Xequence Audio uses standard WebAudio nodes instead: oscillators, filters, gains, delays, convolvers, waveshapers, analysers.

The payoff is that all the actual signal processing happens in the browser's own natively compiled, heavily optimized code (huge shoutout to the Chromium audio team for optimizing this so well).

The downside is a bit less flexibility and a few more weird hacks required to get specialized functionality sometimes.

Dynamic audio graph

A naive implementation might allocate every voice of every instrument up front and leave them running all the time. That is simple, but comes with significant performance problems in larger projects.

In Xequence Audio, the audio graph is dynamic: Nodes are created when a voice needs them and disposed of when it has finished, so the graph is always roughly the size of what is actually audible rather than the size theoretically needed for all instruments at maximum polyphony.

Constructing and tearing down nodes is expensive, so frequently used nodes are cached and reused rather than reallocated.

Scheduling slightly into the future

WebAudio parameter changes applied "now" are applied whenever the control thread happens to get around to it, which would give unacceptably sloppy and inconsistent timing.

Xequence Audio thus schedules every dynamic node configuration and every parameter change at an explicit timestamp slightly in the future, which the audio thread then honours exactly. The result is sample-accurate automation and oscillator / LFO sync (live input is also pushed a small distance into the future rather than fired immediately to inherit the same precise timing / sync).

The codebase

On CoffeeScript: it compiles to readable JavaScript, and running decaffeinate across the whole tree as a single commit before any other work is a perfectly reasonable first move for a new maintainer, as I suspect most would find CoffeeScript too "outdated" (and too elegant 😄).

Pragmatic and also somewhat brilliant

Relative automation

Automation in Xequence Audio is always an offset, from −100% to +100% of the parameter's range, added to whatever the respective fader or slider is currently set to. This means that faders and sliders still work as expected even with written automation, you can rebalance a mix after the automation is written, and the automation just rides along with your change instead of overwriting it. This decision alone removed an entire category of frustration with existing DAWs for me.

The patch is the interface

Hybris, the modular synth, has no separately designed front panel. The modules you add and the way you lay them out are the user interface. That also means that adding a new module type does not require any significant UI work apart from layout out a few (standardized) sliders and buttons.

Caveats for you as a maintainer

Chip in Try the demo