Play Web Audio API AudioBuffer in Howler.js

This article explains how to pass an instantiated Web Audio API AudioBuffer directly to Howler.js for playback. Because Howler.js is designed to load audio from file URLs, it does not officially expose a direct API parameter for raw audio buffers. We will cover the most efficient method to bypass this limitation by injecting your buffer directly into Howler’s internal cache, as well as an alternative standard method using Blobs.


Howler.js maintains an internal cache (Howler._cache) of decoded AudioBuffer objects to prevent redundant network requests. You can exploit this mechanism by manually assigning your AudioBuffer to the cache using a unique dummy string as the key.

When you instantiate a new Howl object with that same dummy string as the source, Howler will pull the buffer directly from the cache instead of attempting to fetch it.

Here is how to implement this:

// 1. Assume you already have a Web Audio API AudioBuffer
const myAudioBuffer = yourExistingAudioBuffer; 

// 2. Define a unique cache key (string)
const cacheKey = 'my-unique-buffer-id';

// 3. Manually inject the AudioBuffer into Howler's internal cache
Howler._cache[cacheKey] = myAudioBuffer;

// 4. Instantiate Howl using the cache key as the src
const sound = new Howl({
  src: [cacheKey],
  format: 'wav', // Provide a dummy format to bypass format-checks
  autoplay: false
});

// 5. Play the sound directly from the memory buffer
sound.play();

Why This Works:

Before Howler.js sends an XMLHttpRequest to load an audio file, it checks if the source string exists in Howler._cache. By pre-populating this cache with your decoded AudioBuffer, Howler skips the download and decoding phases entirely, making the playback instantaneous.


Method 2: Converting AudioBuffer to a Blob URL

If you prefer to avoid accessing Howler’s private APIs (like _cache), you can convert the AudioBuffer into an audio file (like WAV) in memory, create a Blob URL, and pass that URL to Howler.

This method requires helper functions to encode the raw PCM data of the AudioBuffer into a standard WAV format:

// Helper function to encode AudioBuffer to WAV
function bufferToWav(buffer) {
  let numOfChan = buffer.numberOfChannels,
      length = buffer.length * numOfChan * 2 + 44,
      bufferArr = new ArrayBuffer(length),
      view = new DataView(bufferArr),
      channels = [], i, sample,
      offset = 0,
      pos = 0;

  // Write WAV header
  setUint32(0x46464952); // "RIFF"
  setUint32(length - 8); // file length - 8
  setUint32(0x45564157); // "WAVE"
  setUint32(0x20746d66); // "fmt " chunk
  setUint32(16);         // chunk length
  setUint16(1);          // sample format (raw)
  setUint16(numOfChan);
  setUint32(buffer.sampleRate);
  setUint32(buffer.sampleRate * 2 * numOfChan); // byte rate
  setUint16(numOfChan * 2);                     // block align
  setUint16(16);                                // bits per sample
  setUint32(0x61746164); // "data" - chunk
  setUint32(length - pos - 4); // chunk length

  // Write interleaved audio data
  for(i=0; i<buffer.numberOfChannels; i++) {
    channels.push(buffer.getChannelData(i));
  }

  while(pos < length) {
    for(i=0; i<numOfChan; i++) {             // interleave channels
      sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
      sample = (sample < 0 ? sample * 0x8000 : sample * 0x7FFF); // scale to 16-bit signed int
      view.setInt16(pos, sample, true);          // write 16-bit sample
      pos += 2;
    }
    offset++;
  }

  return new Blob([bufferArr], {type: "audio/wav"});

  function setUint16(data) {
    view.setUint16(pos, data, true);
    pos += 2;
  }

  function setUint32(data) {
    view.setUint32(pos, data, true);
    pos += 4;
  }
}

// Usage with Howler.js
const wavBlob = bufferToWav(myAudioBuffer);
const blobUrl = URL.createObjectURL(wavBlob);

const sound = new Howl({
  src: [blobUrl],
  format: 'wav'
});

sound.play();

This approach is highly compatible and does not rely on Howler’s private properties, though it introduces a slight processing overhead to encode the audio data in the browser.