goldfire/howler.js · warning
HTML5 Audio pool exhausted, returning potentially locked aud
Error message
HTML5 Audio pool exhausted, returning potentially locked audio object.
What it means
This is a console.warn (not a thrown exception) emitted by howler.js when the internal pool of reusable HTML5 Audio objects is exhausted. When howler returns a fresh `new Audio()` outside the pool, the browser may not yet have a user-gesture unlock, so play() calls on it can return a rejected Promise (autoplay/lock policy). The library detects this and warns that the returned Audio object may be 'locked' and refuse to play until a user interaction unlocks it.
Source
Thrown at src/howler.core.js:434
/**
* Get an unlocked HTML5 Audio object from the pool. If none are left,
* return a new Audio object and throw a warning.
* @return {Audio} HTML5 Audio object.
*/
_obtainHtml5Audio: function() {
var self = this || Howler;
// Return the next object from the pool if one exists.
if (self._html5AudioPool.length) {
return self._html5AudioPool.pop();
}
//.Check if the audio is locked and throw a warning.
var testPlay = new Audio().play();
if (testPlay && typeof Promise !== 'undefined' && (testPlay instanceof Promise || typeof testPlay.then === 'function')) {
testPlay.catch(function() {
console.warn('HTML5 Audio pool exhausted, returning potentially locked audio object.');
});
}
return new Audio();
},
/**
* Return an activated HTML5 Audio object to the pool.
* @return {Howler}
*/
_releaseHtml5Audio: function(audio) {
var self = this || Howler;
// Don't add audio to the pool if we don't know if it has been unlocked.
if (audio._unlocked) {
self._html5AudioPool.push(audio);
}
View on GitHub (pinned to 1d3053576a)
Solutions
- Ensure the first play() happens inside a user-gesture handler (click/touch/keydown) so the Audio object gets unlocked
- Reduce concurrent HTML5 sounds: pool/sounds, reuse Howl instances, or drop `html5:true` so the default Web Audio path (unlimited voices, no Audio-element pool) is used
- Call Howler volume/unlock or resume the Howler context after a user interaction (e.g. `Howler.volume(Howler.volume())` or play a muted sound on first tap)
- Preload/queue fewer sounds and only create Audio nodes as needed; stop/free `Howl` instances you no longer use
- Treat the warning as non-fatal: it already only fires via testPlay.catch, so gate playback with the promise rejection (`sound.play().catch(...)`) and retry on next user gesture
Example fix
// before
const music = new Howl({ src: ['song.mp3'], html5: true, autoplay: true }); // autoplay at load => locked Audio on mobile
// after
document.addEventListener('click', function start() {
const music = new Howl({ src: ['song.mp3'], html5: true });
music.play();
document.removeEventListener('click', start);
}, { once: true }); Defensive patterns
Strategy: retry
Validate before calling
// Gate any audio start behind a user gesture and check browser unlock support
function canAutoplayAudio() {
const a = new Audio();
const p = a.play();
if (p && typeof p.catch === 'function') {
return p.then(() => true).catch(() => false);
}
return Promise.resolve(true);
}
// usage: if (!(await canAutoplayAudio())) waitForKeyboardOrClickBeforePlaying(); Type guard
function isPlayableAudioResult(result) {
return result !== null && result !== undefined &&
typeof result.play === 'function';
} Try / catch
try {
const id = sound.play();
if (id && typeof id.catch === 'function') {
await id.catch((err) => {
// autoplay/lock rejection: wait for user gesture then retry once
return waitForUserGesture().then(() => sound.play());
});
}
} catch (e) {
// HTML5 Audio element failed entirely: fall back to Web Audio (html5:false)
sound = new Howl({ src: soundSrc });
} Prevention
- Never call play() before a user gesture (click/touch/keydown) on mobile or Chrome autoplay-policy browsers
- Prefer the default Web Audio mode; only use html5:true for streaming/large files where an element pool is required
- Cap concurrent html5 sounds (pool size defaults to ~10) and reuse a single Howl instance with `.play()` per voice instead of creating new Howls
- On first interaction, run a warm-up: `Howler.volume(Howler.volume())` or play a silent sound to unlock audio
- Subscribe to rejections: `sound.play().catch(...)` so locked playback is detected and retried
When it happens
Trigger: Creating more simultaneous HTML5-mode Howl sounds than the pool size (default 10 in HowlerGlobal `_html5AudioPool`), e.g. many `new Howl({html5:true, src:[...]})` instances or rapid `play()` calls on html5 audio; calling play without a preceding user gesture (click/keydown) so the newly created Audio is autoplay-locked; mobile browsers (iOS Safari, Chrome autoplay policy) where audio unlock requires user interaction.
Common situations: Mobile web games/preloaders that spawn dozens of html5:true Howls at page load before any tap; streaming long audio (html5 mode is used for large files/streaming) with many overlapping channels; autoplaying background audio on page load; upgrading howler or switching from Web Audio (default) to html5:true and hitting browser autoplay policies.
Related errors
AI-assisted analysis of goldfire/howler.js@1d3053576a (2026-08-30).
Data as JSON: /api/errors/5a0152930bd70544.
Report an issue: GitHub.