Tone.js

Tone.js 14,743 (https://github.com/Tonejs/Tone.js 14,743 ) (npm 2,036 i tone, MIT, version 15.1.22) puts a digital audio workstation on Web Audio: synths, samplers, effects, and a Transport that schedules loops and sequences in musical time ('8n' is an eighth note). Tone objects wrap native nodes through the standardized-audio-context package and schedule events 0.1 s ahead (the context's lookAhead) so a busy main thread does not delay them.

A reverb-soaked synth arpeggio started from a buttonJavaScript
startButton.onclick = async () => {
  await Tone.start();                                        // unlock audio inside a click
  const synth = new Tone.PolySynth(Tone.Synth).connect(new Tone.Reverb(1.5).toDestination());
  new Tone.Sequence((time, note) => synth.triggerAttackRelease(note, '8n', time),
    ['C4', 'E4', 'G4', ['A4', 'G4'], 'E4', 'D4', 'C4', null]).start(0);
  Tone.getTransport().bpm.value = 96;
  Tone.getTransport().start();
};

The nested array plays two notes in one step. Callbacks fire imprecisely, so always pass their time argument on.