Metering with the Web Audio API

·

When working with audio, there are times when it can be useful to see visualizations of the underlying data.

The most common visualizations tell us how loud the signal is in various ways. A basic peak meter tells us the instantaneous signal level relative to limit of what the system can handle. Originally taking the form of a VU meter, this has been useful for as long as we have been recording and broadcasting sound for level setting.

The web audio api has lots of cool features enabling lots of cool things, from creating synthesizers and drum machines to spatial processing for immersive games. The thing that excited me the most, though, about the web audio API is that it gives us access to the raw buffer data, allowing us to make visualizations with it.

The peak meter shown below has been designed to feel familiar to anyone who has worked with audio before. It has a bar for each channel and its size oscillates with the strength of the signal in the channel. It has a latching peak feature, in which the number at the right tells us the volume in dB of the loudest peak since we last refreshed it. Clicking on the meter refreshes the latches. Inspired by Dorrough meters, the meters also transition from green to yellow to red as the signal level approaches the system’s maximum volume.

Another audio visualization used in this audio player is a waveform. The waveform is just the volume data of the recording plotted as a function of time. This is useful for finding a spot in the recording where things get louder or softer. We can also get a sense of dynamic range from this visualization. The term “Stick of butter audio” has been used to describe audio that has been compressed so severely as to look like a solid rectangle in a waveform visualization. In the figure below, the waveform of each audio example is shown behind the slider for controlling playback location.

Volume-based visualizations need not be limited to just the peak meter and waveform, however. One lesser-used visualization is the goniometer, better known as a vectorscope. In a goniometer, two-channel audio is visualized on an x-y axis, often tilted 45 degrees. This visualization tells us the correlation between the left and right channels, something which is important to know if your two-channel audio might ever be played back in mono.

The first example in the figure below shows an audio file with a basic sine wave on the left channel and silence on the right channel.

The second example shows a sine wave on the right channel and silence on the left.

The next example shows the exact same sine wave on both channels. This audio could be described as “perfectly correlated”; it could also be described as mono, because if this file only contained one channel of data, the resulting sound would be the same.

This next example shows sine waves in the left and right channels with their polarity reversed. One would get the same result by delaying one channel by the period of the wave, so that the two channels would be 180 degrees out of phase with each other. When mixing stereo audio that might someday be folded down to mono, this is exactly what you don’t want to see in your vectorscope.

My personal favorite visualization using the goniometer is when you have two sine waves that are 90 degrees out of phase with each other. I can’t think of any reason why this would happen in the real world, but I find this example cool, so I’m including it.

Finally, the last two examples are of real-world audio visualized in a goniometer. The first is an excerpt from a song by Chvrches titled “The mother we share”. I chose it because it features a ping-pong panning effect that I think shows off the meter.

The last example is an excerpt from the Marine Chamber Orchestra performing Brahms Serenade No. 1 in D, Op. 11.

Fig. 1. Demo of Web Audio API Metering

If you would like to use these visualizations in your web sites, please do! I have shared the source code to them on github as both vanilla javascript, and React components. If you do use them, I’d love to hear about it. Also please let me know if you find any bugs with them or ways in which they might be more useful to! An email to [email protected] is the best way to reach me. Thanks for reading!

View from near the top of Edinburgh Castle

preventDefault is an anti-pattern

·

On a large number of sites and in quite a few code bases, I’ve seen an html/js combination that looks something like this:

<a href="#" id="unique">Click Me</a>

<script>
var ele = document.getElementById('unique');
ele.addEventListener('click', function(event) {
  event.preventDefault();
  window.alert('Thanks for clicking!');
});
</script>

This use of preventDefault is tempting thing to write if the element you want to use for javascript interactivity is something that you want to have styled like the rest of the links on the page. However, to the person considering using that pattern, I would ask the question “If you don’t want the user to be taken to a new URI, why are you using an anchor tag?”

I would argue the following code is no more cumbersome to write, avoids some tricky bugs, and has the benefit of being more correct:

<style>
.link-like {
  color: blue;
  text-decoration: underline;
  cursor: pointer;
}
</style>

<span id="unique" class="link-like">Click Me</a>

<script>
var ele = document.getElementById('unique');
ele.addEventListener('click', function(event) {
  window.alert('Thanks for clicking!');
});
</script>

A guiding principle I use when I design front-end projects is:

  • HTML is what it “is”.
  • CSS is what it looks like.
  • JS is how it should behave.

In the first example we were letting how we wanted an element to look drive what it “is” and as a result we were complicating our code for how it should behave. By separating our concerns, we allow each part of our HTML, CSS, and JS code do what it was meant to do.

Another common time when preventDefault is used is to override the submit button in a form. This is where things get a little more tricky, because the developer might be using javascript to perform some client-side validation before allowing the form to be submitted. I think a good way of determining if this is a good time to be using preventDefault is asking “what should happen to the user that has javascript disabled?”.

Even in that instance, preventDefault may not be what we want though. For the cases where preventDefault allows us to provide progressive enhancement, we could use javascript to disable the submit button and provide helpful feedback to the user about why the form contents aren’t yet valid.

Thanks for reading! If you have thoughts (particularly if there is a valid use-case I haven’t thought of), I’d love to hear them.

San Juan Island