var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); function loadSound(url) { return fetch(url) .then(response => response.arrayBuffer()) .then(buffer => audioCtx.decodeAudioData(buffer)) .then(audio => new Sound(audio, url)); } function SFXManager() { this.soundMap = {} } SFXManager.prototype.loadSound = function(name, url) { var self = this; return loadSound(url).then(sound => { self.soundMap[name] = sound; }) } SFXManager.prototype.play = function(name) { this.soundMap[name].play(); } SFXManager.prototype.unloadSound = function(name) { delete this.soundMap[name]; } function Sound(buffer, url) { this.buffer = buffer; this.url = url; this._target = audioCtx.destination; this._source = null; this._playStartTime = 0; this._timeToStartFrom = 0; this._playing = false; } Sound.prototype.setTarget = function(target) { this._target = target; if(this._playing) { this.pause(); this.resume(); } } Sound.prototype._start = function(offset) { this._source = audioCtx.createBufferSource(); this._source.buffer = this.buffer; this._source.connect(this._target); this._source.onended = function() { this._playing = false; this._timeToStartFrom = 0; }.bind(this); this._playStartTime = audioCtx.currentTime; this._timeToStartFrom = offset; this._playing = true; this._source.start(0, this._timeToStartFrom); } Sound.prototype.play = function() { if(this._playing) { this.pause(); } this._start(0); } Sound.prototype.resume = function() { this._start(this._timeToStartFrom); } Sound.prototype.pause = function() { // in case the sound has not been played yet if(this._source == null) return; this._playing = false; this._source.onended = null; this._source.stop(); this._source.disconnect(); var elapsedTime = this._timeToStartFrom + audioCtx.currentTime - this._playStartTime; this._timeToStartFrom = elapsedTime; } var sfxManager = new SFXManager();