# Omnitone: Spatial Audio Rendering on the Web
[![ci][ci-badge]][ci-url]
[![npm][npm-badge]][npm-url]
[![GitHub license][license-badge]][license-url]
Omnitone is a robust implementation of
[ambisonic](https://en.wikipedia.org/wiki/Ambisonics) decoding and binaural
rendering written in Web Audio API. Its rendering process is powered by the fast
native features from Web Audio API (`GainNode` and `ConvolverNode`), ensuring
optimum performance.
The implementation of Omnitone is based on the
[Google spatial media](https://github.com/google/spatial-media) specification
and [SADIE's binaural filters][sadie-url]. It also powers
[Resonance Audio SDK][resonance-url] for web.
- [What's New in 2.0](#whats-new-in-20)
- [Usage](#usage)
+ [FOARenderer](#foarenderer)
+ [HOARenderer](#hoarenderer)
+ [Rotation and Rendering Mode](#rotation-and-rendering-mode)
- [Development](#development)
- [Audio Codec Compatibility](#audio-codec-compatibility)
If you are looking for interactive panning based on Omnitone's ambisonic
rendering, be sure to check out the [Resonance Audio Web SDK][resonance-url]
project!
### Feature Highlights
Omnitone offers __ambisonic decoding__ and __binaural rendering__ of:
- First-order-ambisonic stream (4 channels)
- Higher-order-ambisonic stream (2nd order / 9 channels and 3rd order / 16
channels)
### Omnitone in action:
- __[Omnitone Demos](https://googlechrome.github.io/omnitone/#home)__
- __[Omnitone Examples][examples-url]__
- __[Resonance Audio Web SDK][resonance-url]__
## What's New in 2.0
- **Bundler & SSR support (`#105`):** `build/omnitone.js` and
`build/omnitone.min.js` are now built as UMD modules (working both as a
`
```
Alternatively, install Omnitone as part of your local development via
[NPM](https://www.npmjs.com/package/omnitone):
```bash
npm install omnitone
```
Omnitone provides both ES module and UMD/CommonJS entry points for bundlers:
```js
import Omnitone from 'omnitone';
// or: const Omnitone = require('omnitone');
const audioContext = new AudioContext();
const foaRenderer = Omnitone.createFOARenderer(audioContext);
```
You can also `git clone` the repository and use the library files in `build/`:
```bash
git clone https://github.com/GoogleChrome/omnitone.git
```
### FOARenderer
`FOARenderer` decodes and renders a first-order-ambisonic stream (4 channels).
```js
// Set up an audio element to feed the ambisonic source audio feed.
const audioElement = document.createElement('audio');
audioElement.src = 'audio-file-foa-acn.wav';
// Create AudioContext, MediaElementSourceNode and FOARenderer.
const audioContext = new AudioContext();
const audioElementSource = audioContext.createMediaElementSource(audioElement);
const foaRenderer = Omnitone.createFOARenderer(audioContext);
// Make connection and start play. Hook up the user input for the playback.
foaRenderer.initialize().then(function() {
audioElementSource.connect(foaRenderer.input);
foaRenderer.output.connect(audioContext.destination);
// This is necessary to activate audio playback out of autoplay block.
someButton.onclick = () => {
audioContext.resume();
audioElement.play();
};
});
```
### HOARenderer
`HOARenderer` decodes and renders higher-order-ambisonic streams. Omnitone
supports 2nd and 3rd order ambisonics, which consist of 9 channels and 16
channels respectively.
```js
// Works the same way as FOARenderer. See the usage above.
const hoaRenderer = Omnitone.createHOARenderer(audioContext);
```
### Rotation and Rendering Mode
The rotation matrix in an Omnitone renderer can be updated inside your
application's animation loop to rotate the entire sound field. Omnitone supports
both 3x3 and 4x4 rotation matrices (column-major). Setter calls made before
`initialize()` resolves are preserved automatically.
```js
// Rotation with 3x3 or 4x4 matrix.
renderer.setRotationMatrix3(rotationMatrix3);
renderer.setRotationMatrix4(rotationMatrix4);
```
For example, if you want to hook up a Three.js perspective camera:
```js
renderer.setRotationMatrix4(camera.matrixWorld.elements);
```
Use `setRenderingMode` (or pass `{renderingMode}` to the renderer constructor)
to change the operation of the decoder. This is useful when switching between
spatial media (ambisonic) and non-spatial media (mono or stereo), or when saving
CPU power by disabling the decoder.
```js
// Mono or regular multi-channel layouts.
renderer.setRenderingMode('bypass');
// Use ambisonic rendering.
renderer.setRenderingMode('ambisonic');
// Disable encoding completely (audio processing disabled).
renderer.setRenderingMode('off');
```
## Development
### Building Omnitone Locally
For development, clone the repository and run the following scripts to build the
library. Omnitone uses [Rollup](https://rollupjs.org/) to bundle the sources.
```bash
npm ci # install dependencies.
npm run build # build omnitone library files.
npm run build-doc # build JSDoc documentation.
npm run eslint # run ESLint against source and test files.
```
### Test
Omnitone uses [GitHub Actions](https://github.com/features/actions) and
[Web Test Runner](https://modern-web.dev/docs/test-runner/overview/) for
automated testing. The test suite requires the promisified version of
`OfflineAudioContext`, so it runs in a locally installed Chrome. Running
`npm test` rebuilds the bundles first, so the tests never run against a stale
`build/omnitone.min.js`.
```bash
npm test
```
#### Local Testing on Linux
The test suite requires a Chromium-based browser, so the following setup might
be necessary on Linux distros without one installed.
```bash
sudo apt install google-chrome-stable
```
## Audio Codec Compatibility
Omnitone is designed to run on any browser that supports Web Audio API; however,
it does not address incompatibility issues around media codecs across browsers.
Decoding compressed multichannel audio with more than 3 channels via `