Introducing Sonorous.js: Harmonizing Web Audio

The eko Devs
Dec 28, 2020 · 5 min read
(Illustration by Itai Raveh)

Web Audio is admittedly very powerful — you can craft an interactive musical journey, influence the ambiance, or recreate an instrument built in the 1920s. Web audio can also be a huge headache.

For starters, the API is low level and complex. For the simple background music or sound effect, web audio can feel like trying to take a sip of water out of a firehose. (For the record, it should stay that way — to build complex audio experiences, you need something that offers a lot of control.) While there are libraries out there that offer abstractions over web audio, we’ve found them old and outdated. A coworker of mine once said, “Whenever an audio plugin related bug is discovered, instead of quickly fixing it and moving on, we need to spend a whole day debugging internal implementation [of our 3rd party library].”

I’m here to say that there’s a new way forward. Over the last six months, I’ve been working on Sonorous- a new open source audio library. Sonorous is easy to use, clear to follow, and built with today’s standards in mind.

Sonorous abstracts the complexity of web audio.

Just to start playing a sound, web audio requires several set up steps.

Sonorous reduces this to 3 lines of code.

Sonorous provides other major advantages other than ease-of-use. If the web audio APIs weren’t tricky enough to understand on their own, there’s the added complexity of each browser implementing the APIs differently. For example, Chrome may allow you to call stop() on a source node before it’s been started, but Safari will throw an error and crash your program. On the other hand, sometimes Safari will fail to initialize an AudioContext but won’t throw an error, and Chrome would. In many browsers now, you are unable to start playing audio without a user interaction first. This is just the tip of the iceberg, but Sonorous handles all of this for you. It’s abstracted by the library, so your code can remain clean and concise.

(Illustration by Itai Raveh)

Before Sonorous, we used Howler as our audio library for many years. It’s the gold standard and has 50k weekly downloads (according to npm). That being said, there have been a lot of changes to JavaScript/the browser, and I wanted to take advantage of that. For one, web audio is now supported in every modern browser, so Sonorous doesn’t deal with HTML5 audio at all. Web audio is superior in every way; HTML5 audio is pretty old and outdated and doesn’t offer all the flexibility that web audio has. I also wanted to leverage the more modular ES6 class structure to create extensible architecture with clear interfaces. Debugging cross-browser issues could be tricky with Howler, but Sonorous’s architecture was designed to make it easy to pinpoint and fix issues. The current implementation could also easily be expanded in the future as web audio features grow.

(Illustration by Itai Raveh)

While well-defined architecture is thrilling (to me, at least), I’m more excited to talk about Sonorous’s memory management. Audio buffers, once decoded, can be huge. Unfortunately, audio files must be decoded to be played back. And with Howler, as soon as the sound is loaded, the decoded buffer is kept in memory at runtime. 60 seconds of uncompressed audio translates to 23MB of memory at runtime. If your program contains just 20 minutes’ worth of audio files, all loaded at once, that’s over 460MB of memory, which is more than enough to cause an iOS Safari tab to crash instantly. Sonorous gives you the option of storing the encoded buffer or the decoded buffer when the sound loads. If you store the encoded buffer, it would only be decoded when play() is called. Once the play operation finishes, the decoded buffer would be removed again. There might be a lag before playing if the audio file is large, but this solution offers more control over how much memory this library uses when not playing a sound.

At eko, we integrated Sonorous into our audio plugin. Any sound effects or background music played in any of our videos now come through Sonorous. There’s a simple bridge in place for backwards compatibility- any projects that assume the audio plugin uses Howler will receive a wrapped version of Sonorous that matches the Howler API. The audio plugin also adds functionality on top of Sonorous- the ability to link the sound effect playback to video playback (play when the video plays, pause when the video pauses), the ability to load sound effects relative to when they need to be played in the video, etc. Head to eko.com and watch any video to see Sonorous in action!

Sonorous is an easy to use audio library for the modern age. Whether you’re building a complex game with lots of audio or just adding background music to your website, Sonorous can help you. It hides the complications of working with audio across browsers and is built with the modern browser in mind. If you’re already using Howler, the switch to Sonorous should be pretty painless. Sonorous is just getting started, and I’m excited to share it with you. If you’re as interested in this as I am, feel free to contribute! Sonorous is open source, and we welcome any and all looking to participate in Sonorous going forward.

If you’re curious to learn more, check out the documentation. And if you’d like to see more examples in action, check these out.

ekoEngineering