JSFiddle - React, Tailwind, and code Playground
HTML
<ul>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-leadguitar.mp3">Lead Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-drums.mp3">Drums</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-bassguitar.mp3">Bass Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-horns.mp3">Horns</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-clav.mp3">Clavi</a></li>
</ul>
JavaScript
let active_source = null;
let buffers = {};
const context = new(window.AudioContext || window.webkitAudioContext)();
let offset = 0;
const tempo = 3.074074076;
const tracks = document.getElementsByClassName('track');
function playTrack(url) {
let buffer = buffers[url];
let source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.loop = true;
if (offset == 0) {
source.start();
offset = context.currentTime;
active_source = source;
} else {
let relativeTime = context.currentTime - offset;
let beats = relativeTime / tempo;
let remainder = beats - Math.floor(beats);
let delay = tempo - (remainder*tempo);
let when = context.currentTime+delay;
stopActiveSource(when);
source.start(context.currentTime+delay,relativeTime+delay);
active_source = source;
source.onended = function() {
active_source = null;
};
}
}
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e) {
playTrack(this.href);
e.preventDefault();
});
getBuffer(tracks[i].href);
}
function getBuffer(url) {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(evt) {
context.decodeAudioData(request.response, store);
};
request.send();
function store(buffer) {
buffers[url] = buffer;
}
}
function stopActiveSource(when) {
if (active_source) {
active_source.onended = null;
active_source.stop(when);
}
}