How to integrate Superpowered
The Superpowered library is designed for application code to be used across multiple platforms. Language specific syntax aside, the API will look mostly the same across platforms and most importantly, Superpowered audio will be absolutely audibly identical across platforms. In other words, your filters and compressors will sound the same, no matter which OS/platform they’re running on.
Make sure you're being shown the correct language by toggling the language selector on the top right.
Licensing
The SDK may be evaluated for free, so you can get started with the library and start making some amazing products.
Please see our Licensing page for more information
Javascript introduction
For the purposes of this guide, we'll be using vanilla ES6 Javascript to demonstrate how to get started with the Superpowered SDK. Using Superpowered in Javascript requires the use of WebAssembly to achieve the blazing speed that Superpowered is capable of. Setting up Javascript to handle realtime low latency audio with WebAssembly might be a little outside of what you're used to as it requires thinking about using multiple threads and WebAssembly Linear Memory. Don't worry, we've made it easy to understand.Please note that setting Superpowered up correctly allows more of the code you write to be transferred between JS and C++ with as few syntax changes as possible.
What is WebAssembly?
WebAssembly is a rapidly evolving technology with great momentum. It allows for high-performance data processing in the browser. In short: what needed to be done in a native app, can now be possible in the browser. We've been able to take the Superpowered C++ SDK and port it to WebAssembly, bring all the great unique features you know and love to the web.
Within the last few years WebAssembly has become very widely supported, to a point where it is now a viable technology to use in production. If you'd like to see the wide range of browsers supported, see here.
What about the Web Audio API?
Superpowered uses the Web Audio API to set up audio I/O in the browser. But the Web Audio API has inconsistencies and slightly different DSP implementations across browsers/devices which mean you can't guarantee your great product will perform and sound identically across devices. One reason why you need Superpowered!
Superpowered runs within the Web Audio API's AudioWorklets, that are scheduled within a standard WebAudio AudioContext
and can be used in standard WebAudio API nodes. This means you can leverage API such as getUserMedia
and the MediaSourceNode
to get live microphone signals into the Superpowered ecosystem. Once within the superpowered ecosystem you can perform all of your processing chain in one AudioWorkletProcessor
, or chain multiple AudioWorklet
s together in series or parallel.
Loading and serving the library
Since Superpowered v2.6.4
, the library can be installed via NPM. This simplifies the process of getting up and running with Superpowered and it's associated parts.
npm install @superpoweredsdk/web@VERSION_NUMBER// so avoid using// npm install @superpoweredsdk/web// or// npm install @superpoweredsdk/web@latest
The reason we suggest you include the specific version is to prevent potential version mismatches as our library gets updated. You'll be pulling in Superpowered library code into both your main javascript application AND your AudioWorkletProcessor scripts, so it's important we ALWAYS have exactly the same version of Superpowered being installed and bundled from you package.json and the one being pulled down at runtime from a CDN (or hosted yourself).
So with our latest release we'd run
npm install @superpoweredsdk/web@2.7.2
and in our main javascript application, we'd import with
import { SuperpoweredGlue, SuperpoweredWebAudio } from '@superpoweredsdk/web' // which is coming from our installed package in node_modules
Then in your AudioWorkeltProcessorScripts, pull the excact same version from the CDN at runtime (or host yourself)
import { SuperpoweredWebAudio } from "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.7.2";
ES2020 syntax
Please also note that we're using the ES2020 standardized private class fields in our Javascript library code now as part of our effort to modernise our code. We've found that some older webpack/babel configurations are not setup to work with the newer syntax out of the box, so if that's the case you'll need to add the required plugins to your setup.
Alternatively, you can also avoid the troublesome transpiling and bundling by importing the Superpowered modules at runtime with a URL rather than importing your the installed library in your package.json
.
If you'd like to import at runtime on your main thread, instead of importing from the locally installed @superpoweredsdk/web
in your node_modules
folder, use a URL for the import path instead. For example:
import { SuperpoweredGlue, SuperpoweredWebAudio } from 'https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.7.2';
Evaluation vs production
Superpowered requires an additional WebAssembly binary asset and a publically accessable copy of the JS library to function. To streamline the evaluation of Superpowered, a CDN hosted copy of the libraries WebAssembly is automatically fetched at runtime. However, note that this is the evaluation version of the Webssembly and is hardwired to use the evaluation license key.
To run Superpowered in production, you will need to host these two files yourself and not utalise the CDN. This removes the 3rd party dependency for your application to run.
The two files you'll need to host in production are
Superpowered.js - available for download at https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@latest/dist/Superpowered.js superpowered.wasm - available for download at https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@latest/dist/superpowered.wasm
Starting Superpowered
Although Superpowered can be loaded into an existing audio context, we recommend your application's main AudioContext
is created by the method we provide in the SuperpoweredWebAudio
helper class. This extended AudioContext
offers useful tools to make sure you can get audio in and out of Superpowered efficiently and with ease across browsers. Behind the scenes, you'll still have a standard AudioContext
which you can use elsewhere in your application if required.
To load Superpowered into your Javascript application, the following steps should take place in order:
- Initialize the Superpowered WebAssembly with passing your license details (and WASM location is you are running in production). Note the async nature of this method.
- Create an application wide WebAudioManager with the
SuperpoweredGlue
wrapper, passing in both the required samplerate and the instantiated WebAssembly.
In ES6 Javascript, that looks like this:
// Make sure to include the version number here to prevent compatability issues with your AudioWorklets!import { SuperpoweredGlue, SuperpoweredWebAudio } from '@superpoweredsdk/web@2.7.2';const minimumSampleRate = 44100;// Option 1: If running the evaluation version of the libraryconst superpowered = await SuperpoweredGlue.Instantiate('ExampleLicenseKey-WillExpire-OnNextUpdate');// Option 2: If running the Superpowered in production with a license keyconst superpowered = await SuperpoweredGlue.Instantiate('[YOUR_LICENSE_KEY]', '/[WHERE YOU HOST]/superpowered.wasm');const webaudioManager = new SuperpoweredWebAudio(minimumSampleRate, superpowered);
After that, you'll have access to the helper methods under webaudioManager
, and the standard AudioContext
it sits on will be under webaudioManager.audioContext
, with all of the standard WebAudio APIs available such as webaudioManager.audioContext.createGain()
or audioEngine.webaudioManager.audioContext.currentTime
for example.
Let's make some noise
We're going to keep things simple, so we'll be setting up a Superpowered Generator to create a sine tone, then pass that to the speakers. Make sure you turn down your speakers, unless you have a particular thing for a mathematically perfect A below middle C.
Setting up the required parts
AudioWorkletProcessor script
First, we need to create (and serve up) an AudioWorkletProcessor which will house a Superpowered Generator instance.
AudioWorkletProcessors operate on a separate, real-time audio thread, this is what makes them suitable to feed the audio stack with an uninterrupted and steady flow of data. But because of this, they are isolated and operate outside of the main Javascript thread. They should be fetched by the browser at runtime and cannot be bundled like traditional Javascript files.
// toneProcessor.js// Import the superpowered library from the CDN if evaluating// Make sure to include the version number here to prevent compatability issues with your main thread!import { SuperpoweredWebAudio } from "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.7.2";// Or from your own location if in production eg// import { SuperpoweredWebAudio } from '/[YOUR_OWN_LOCATION]/Superpowered.js';class ToneProcessor extends SuperpoweredWebAudio.AudioWorkletProcessor {// Runs after the constructor.onReady() {// Create an instance of a SP generator class.this.generator = new this.Superpowered.Generator(this.samplerate, // The initial sample rate in Hz. this.samplerate is inherited from the extended SuperpoweredWebAudio.AudioWorkletProcessor class.this.Superpowered.Generator.Sine // The initial shape.);this.generator.frequency = 440; // Generate a simple test tone at A4.this.genOutputBuffer = new this.Superpowered.Float32Buffer(2048); // An empty array to store the mono output of the generator.// Pass an event object over to the main scope to tell it everything is ready.// This is the callback defined as the final argument in webaudioManager.createAudioNodeAsync in the main thread.// Here we use it to tell the main thread that audio I/O is ready and processing audio.this.sendMessageToMainScope({ event: 'ready' });}// onDestruct is called when the parent destruct() method is called.// You should clear up all Superpowered object instances here.onDestruct() {this.generator.destruct();this.genOutputBuffer.free();}processAudio(inputBuffer, outputBuffer, buffersize, parameters) {// Generates the next chunk of the sine wave.this.generator.generate(this.genOutputBuffer.pointer, // Output, pointer to floating point numbers. 32-bit MONO output.buffersize // Number of frames.);// Next we need to convert the mono output of the generator into the interleaved stereo format that the AudioContext expects.this.Superpowered.Interleave(this.genOutputBuffer.pointer, // Left mono input.this.genOutputBuffer.pointer, // Right mono input.outputBuffer.pointer, // Stereo output - this is routed to the AudioWorkletProcessor output.buffersize // Number of frames.);}}// The following code registers the processor script for the browser, note the label and reference.if (typeof AudioWorkletProcessor !== 'undefined') registerProcessor('ToneProcessor', ToneProcessor);export default ToneProcessor;
The AudioWorkletProcessor file(s) will need to be served up to the client side application at runtime, much like the Superpowered WebAssembly library above. For this example we'll have the toneProcessor.js
file at http://localhost/static/processors/toneProcessor.js
.
Note that the
AudioWorkletProcessor
is not bundled with your source code and it pulls its own copy ofSuperpoweredWebAudio
from the statically served Superpowered library files. It's important to understand where files are being served from here.
Main script
Earlier we set up Superpowered and got it initialized in our main Javascript scope. Next we need to load our AudioWorkletProcessor toneProcessor.js
into the WebAudio AudioContext to be scheduled.
We've created a handy wrapper function called createAudioNodeAsync
for this in the SuperpoweredWebAudio
helper class. We pass the location of the processor file (served statically), the name of the processor it gets registered with, and a reference to a function that will handle all incoming messages from the audio thread.
// ... webaudioManager defined above in previous steps// The location of the processor from the browser to fetch.const noiseProcessorUrl = 'http://localhost/static/processors/toneProcessor.js'// Define a handler that will be called whenever this.sendMessageToMainScope is called from the AudioWorkletProcessor scope.// Remember we called it with a ready event so expect to see it here.const onMessageProcessorAudioScope = (message) => {if (message.event === 'ready') {console.log(message);}}// Now create the AudioWorkletNode, passing in the AudioWorkletProcessor url, its registered name (defined inside the processor) and the message callback.const generatorProcessorNode = webaudioManager.createAudioNodeAsync(toneProcessorUrl, 'ToneProcessor', onMessageProcessorAudioScope);// Connect the AudioWorkletNode to the WebAudio destination (main audio output by default, such as your speakers).generatorProcessorNode.connect(webaudioManager.audioContext.destination);
See it all running
We created an ES6 sandbox with everything all hooked up for you. We'll be using this as the starting point for our guides later on. Fork the code sandbox and have fun!
Code examples
You can find example applications for common Javascript frameworks (React, Angular and Vue) having Superpowered loaded in and ready to roll here:
Now you've Superpowered up and running, so let's start doing some amazing things! Take a look at our guides for help with common tasks and setups you might require for your product:
Native apps introduction
Real-time audio I/O and the overall developer experience can be quite different between the operating systems supported by Superpowered. We provide open-source top level wrappers that iron out most of those differences and provide a near identical API to interact with system audio.
Integration guides
The following integration guides below will help you get off the ground with Superpowered on the various platforms.