Home

Web Audio Madness: (era era era) Setting a negative playback rate on an AudioBufferSourceNode

Author: Andrew Gallagher

Published at: Thurs Jan 23 2025

This work is now part of the open-source library simple-reversible-audio-buffer-source-node.


In the Requirements and Use Cases of the Web Audio API spec, outlined over a decade ago in 2013, the Audio Working Group described the possibility of creating a connected DJ booth application. The following DJ workflow is detailed:

…the DJ would be able to quickly select several other track [sic], play them through headphones without affecting the main audio output of the application, and match them to the track currently playing through a mix of pausing, skipping forward or back and pitch/speed change.

This is (mostly1) possible now! Some parts of this application are relatively straightforward:

But one crucial feature of a DJ application that is presently nontrivial to implement is the ability to audibly rewind an audio track. Audibly rewinding a track has two critically important functions for a DJ:

  1. while cueing, the DJ can precisely hear where a cuepoint will start without needing to play a track forwards or consult a visual representation of the audio. Having an accurate and fast way to define cuepoints is essential for beatmatching as defined in the spec's use case.
  2. the DJ can perform a spinback of the track. In some genres of music, rewinding a track in this way is an essential transition technique.

The naïve approach of setting a negative playbackRate is only supported in some WebKit-based browsers, so Chrome users — 65% of internet traffic — are out of luck.

Fortunately, it is possible to implement audible rewind. Like most programming problems, this can be broken up into a set of smaller, simpler problems.


High-level overview

This work abstracts out a bit more down the line to be an AudioNode that generally supports a negative playbackRate, but for right now we'll specifically work with our little DJ example.

What we're essentially looking to create is an AudioNode that can generate the "era era era" sound that is characteristic of DJing. We'll need to have a reversed version of our audio, and we'll want the ability to quickly swap between the forwards and reversed audio. When we swap between forwards and reversed audio, we'll want to be very precise about the start point of the audio that is being swapped in, so that we don't have any audio jank.

This sets us up for four distinct tasks:

  1. Reversing our forwards audio
  2. Accurately tracking playback position of audio
  3. Swapping between forwards and reversed audio
  4. Putting it all together

1. Reversing our forwards audio

Our easiest task is reversing our audio — of which there are a few different approaches:

At a very high level, all digital audio just an array of floating point numbers. We just need to reverse this array.

2. Accurately tracking playback position of audio

There is not a great, built-in way to track the playback position of an AudioBufferSourceNode. For our DJ application, the typical minimal approach of using audioContext.currentTime to bookend when audio starts and stops would be too imprecise to prevent audio jank and may cause misalignment between our forwards and backwards audio tracks. Besides, detuning and adjusting playbackRate for beatmatching would mean we would have to perform precise math when calculating the amount of time played between these already imprecise bookends.

Most of the more clever workarounds to this are problem, of course, in a random GitHub thread. My favorite approach detailed here is to create an additional channel on the AudioBuffer, fill it with the percentage that a channel completed with 0 at the first index and 1 at the last index, and then read from it via an AnalyserNode.

To elaborate briefly on this, an AudioBuffer is composed of channels of Float32Array representing the amplitudes of an audio signal. There are no rules to what these Float32Arrays can actually encode, assuming that the values we put in the array are finite 32-bit floating-point numbers. When we play digital audio, your computer traverses through this array of numbers and translates that into sound. If we have a silent, additional channel that represents playback percentage, we can figure out what this current playback percentage is at a given time by reading from this channel during playback.

3. Swapping between forwards and reversed audio

Swapping between forwards and backwards audio turns us back into the more straightforward world of good old state management. When our playbackRate is positive, we want our forward node to be playing. When we set our playbackRate to negative, we want our reverse node to be playing. Finally, when we swap between directions, we want to make sure that our nodes times are aligned. The playback position of the reverse node is the complement of the forward node, and vice versa — as an example: given a 10-second long audio clip, if we are exactly 4 seconds into forwards playback and we move into reverse, we'll want to play the reverse audio from 6 seconds in.

To handle this swap, we'll override the playbackRate method of an AudioNode. When the amount is negative but our forwards node is currently playing (or vice versa), we swap in the active node with the complementary playback position of the inactive node and pause the inactive node.5

Both nodes could be routed through a ChannelMergerNode, so our exposed interface may just be a single AudioNode.

4. Putting it all together

After wiring everything together, we're left with something like this demo.6

Press play audio to begin playback of a drum track.
Putting the playbackRate in the negative will play the drum track backwards.

-2 2
Current rate: 1

By mapping playbackRate to an interface representing a jog wheel or turntable, a user could rapidly toggle through a track and create that sweet "era era era" sound.


  1. iOS still does not support having multiple simultaneous audio outputs, so advice for mobile browser-based DJ applications is to use a splitter, which, to me, is ASS.
  2. Pro tip when you're pausing an AudioBufferSourceNodes is to set the playbackRate to 0.
  3. For an example illustrating how to reverse channel data, see this implementation.
  4. For an example of routing a playback percentage channel through an Analyser, see this implementation.
  5. For an example of swapping between forwards and reverse nodes, see this implementation.
  6. For demo source code, refer to this repo.