How to Check if Howler.js Has Loaded All Audio

When building web applications with interactive audio, ensuring your sound assets are fully loaded before playback prevents latency and broken user experiences. This article explains how to determine if the Howler.js library has finished loading its assets using instance state checks, event listeners, and Promise-based batch loading for multiple files.

1. Using the onload Callback

The most direct way to check when a specific Howl instance has finished loading is by using the onload callback option during initialization.

const sound = new Howl({
  src: ['audio/track.mp3'],
  onload: function() {
    console.log('The audio file has finished loading!');
  },
  onloaderror: function(id, error) {
    console.error('Failed to load audio:', error);
  }
});

2. Registering the ‘load’ Event Listener

If the Howl instance has already been created, you can register a listener for the 'load' event dynamically using the .on() method.

const sound = new Howl({
  src: ['audio/track.mp3']
});

sound.on('load', () => {
  console.log('Asset loaded successfully.');
});

3. Checking the State Programmatically

You can query the current loading status of any Howl instance at any point in your application using the .state() method. This method returns one of three strings: 'unloaded', 'loading', or 'loaded'.

if (sound.state() === 'loaded') {
  console.log('Audio is ready to play.');
} else if (sound.state() === 'loading') {
  console.log('Audio is still downloading...');
}

4. Tracking Multiple Assets with Promises

If your application relies on multiple audio assets, you can wrap the Howler.js load events in JavaScript Promises. This allows you to use Promise.all() to determine when every single asset is fully loaded.

const audioFiles = [
  'audio/background.mp3',
  'audio/click.mp3',
  'audio/explosion.mp3'
];

// Helper function to wrap Howl in a Promise
const loadAudio = (src) => {
  return new Promise((resolve, reject) => {
    const sound = new Howl({
      src: [src],
      onload: () => resolve(sound),
      onloaderror: (id, error) => reject({ src, error })
    });
  });
};

// Load all assets in parallel
Promise.all(audioFiles.map(loadAudio))
  .then((loadedSounds) => {
    console.log('All audio assets have successfully loaded!');
    // You can now safely start your application or game loop
  })
  .catch((err) => {
    console.error(`Error loading audio asset (${err.src}):`, err.error);
  });