Creating the WebAssembly version of Superpowered Audio

We're happy to announce that developers can now use Superpowered audio features in JavaScript without the need of building, initializing or even touching WebAssembly. All WebAssembly magic and complexity happens conveniently inside Superpowered.

WebAssembly

Releasing a complex development framework in WebAssembly will always be challenging. A big API like Superpowered cannot be just compiled with Emscripten, and cross your fingers, let's hope everything will just work. Not to mention, some C and C++ concepts don't work well (or at all) in a JavaScript context.

Here is a recap of what we did this summer to deliver the Superpowered Web Audio JavaScript and WebAssembly SDK for you.

Threading and Shared Memory for Audio

Some Superpowered features have a background thread to offload blocking operations (for example writing to disk with the recorder or decoding audio in the advanced player).

Web browsers have Workers to create background threads, but spawning background threads inside WebAssembly is not a standard feature yet. Chrome is leading the way with pthreads support, but it's an experimental feature so far.

Creating a complex JavaScript thread management detour is theoretically possible (WASM could call that), but it doesn't make sense to create because direct communication between multiple threads via memory is not possible cross-browser currently.

The SharedArrayBuffer API would offer exactly that, but due to the famous Meltdown and Spectre attacks most browsers have it disabled, only Chrome and Firefox still support it, as of today.

Threading and shared memory limitations require us to re-architect the recorder, the live analyzer and the advanced audio player, therefore they are not included in the very first version of this SDK.

Frequency domain transformation and therefore time stretching and pitch shifting also require background threading support to allocate memory in a non-blocking way, and we can ship these features now with the help of the fixed linear WASM memory.

Output Arguments

We are using the Embind feature of Emscripten to bridge Superpowered C++ classes to JavaScript. We can keep the C++ API and the JavaScript API congruent this way, which is critical for us to help ease of understanding, adoption by developers and of course, keep our support queues reasonably light.

Embind doesn't support input/output or output arguments (pointers to single values) like this:


float someOutputValue;
someObject->doSomething(&someOutputValue);

We had to replace many APIs with getters to avoid them. It turned out that eliminating output arguments with getters helps ease of use, because it doesn't require the developer to memory-manage any arguments, reducing the risk of crashes or memory leaks due to bad code.

Previously, we also used some old C++ concept of "read-only" properties, while there is no way in C++ to make a public property read-only, so it was only a "good faith" concept:


class someClass {
public:
    float someProperty; // READ-ONLY, please do not write this!
};

We replaced all "read-only" properties with getters, therefore "accidentally" overwriting them is not possible anymore. It's crystal clear now which property is read-write (all of them!) and which property is "read-only" (none of them, use getters):


someObject->someProperty = 1.0f; // read-write
float value = someObject->getAnotherProperty(); // read-only

Destructors

JavaScript has no destructors and therefore cannot notify WebAssembly that it needs to deallocate some memory. The explicit .delete() method by Embind doesn't release our custom additions. We had to implement specific destruct() methods for each class to release memory, for example:


let mixer = Superpowered.new('StereoMixer');
mixer.destruct();

Arrays in the linear memory

You may notice the strange constructor in the example above: Superpowered.new()

We had to create this unique constructor wrapper to create convenient access to array properties, array arguments or array returns, because Embind's support is very limited. While Float32Arrays can be created around regions in the linear memory, it's far from our guiding principle of an easy-to-use API.

So now this:


class StereoMixer {
public:
    float inputGain[8];
};

Can be accessed in JavaScript like this:


mixer.inputGain[2] = 0.5;

Much more convenient than creating a Float32Array around a reference to inputGain.

Web Audio + WASM + Superpowered

It makes great sense to use Superpowered features, especially effects in Web Audio's AudioNodes. We made a few helper APIs and polyfills into our SDK to effortlessly create audio buffers, AudioContext and AudioNodes with Superpowered support in a cross-browser way.

It supports both the old ScriptProcessorNodes (we're looking at you, Safari) and the current Audio Worklets.

Because of audio processing efficiency, AudioNodes extended with Superpowered operate on stereo interleaved audio buffers instead of non-interleaved buffers of "regular" AudioNodes.

Examples! New Kind of Documentation

We designed a new kind of documentation for the Superpowered Web Audio JavaScript and WebAssembly SDK.

We were not satisfied by the looks and complexity of any existing JS documentation framework. The Superpowered API is really big and those frameworks produced a hardly understandable structure.

We're not happy with our native documentation by Doxygen either, but we hope this new documentation format will satisfy your needs and we can change that to something similar.

Examples are the best way to show any API. Boring "classic" documentation is often hard to understand and is bad at illustrating the the "big picture".

This new documentation has all features documented by pseudo-examples. Pieces of commented code that you can use almost immediately with copy and paste.

We hope you will enjoy this new documentation format. We're eager to hear your thoughts about it at hello@superpowered.com

Licensing, Emscripten Bitcode

Just like with native applications, license keys for Superpowered WebAssembly must be created at our developer portal: https://superpowered.com/dev

We are currently licensing Superpowered Web Audio JavaScript and WebAssembly SDK on a case-by-case basis and can offer free licenses for certain projects. Also, if there is also a need to use Superpowered components in custom WebAssembly builds, we offer an Emscripten Bitcode version for this.

Please contact us at hello@superpowered.com for any licensing questions as well as licensing an Emscripten Bitcode version of Superpowered.

Next Steps

WebAssembly is an emerging technology with tremendous momentum and promise. While all major browser supports it already, it's still a work in progress.

Therefore we were not able to deliver the following native Superpowered features in the first version of the Superpowered Web Audio JavaScript and WebAssembly SDK due to time constraints (consider the features below under development):

Upcoming

We've partnered with a world-famous rockstar for our Superpowered Web Audio WebAssembly and JavaScript SDK demo -- it will be out for public consumption in a few weeks.

Not to mention, we'll be launching a browser latency test page soon too.

Lots of exciting stuff coming at you, we cannot wait to see and hear what you all build.

Try it now

See All Superpowered Audio Tech Blog Content