gapless.js

A gapless playback javascript library

gapless.js is a javascript library for playing recorded concerts, to suit our needs over https://relisten.net. It is driven by a state machine and is completely headless (you design your own UI) and library agnostic.

It is not intended to be a universal solution to all use-cases, but specifically to solve playing back concerts and similar “song”/“album” style playback. Meaning, it’s not designed for short loops (though it could support that).

This library is over a decade old at this point, though we completely re-wrote it from scratch in the past year. It takes inspiration from Gapless 5 by Rego Sen, who pioneered these techniques on the web. We wrote our own after we had slightly different needs, like playing back concerts and a headless UI.

Plain <audio /> tags (non-library)Not gapless

Plain <audio>

play() the next file on ended

gap

0:00

gapless.jsGapless

gapless.js

Starts instantly over HTML5 Audio, then hands off to a decoded Web Audio buffer.

gap

part-1.wav

Not loaded

part-2.wav

Not loaded

The next file is scheduled on the AudioContext clock to start on the exact sample the first one ends.

0:00 / 0:10

A complete gapless.js concert

A full set

Grateful Dead, Cornell University, May 8, 1977 — set one, eleven tracks.

New Minglewood Blues

1 / 11

0:00

–:––

Press play. Audio starts right away over HTML5 while tracks decode in the background.

Debug

queue=idle track=— output=— buffer=—

    Quick start

    import Queue from 'gapless';
    
    const player = new Queue({
      tracks: [
        'https://example.com/set1-01.mp3',
        'https://example.com/set1-02.mp3',
        'https://example.com/set1-03.mp3',
      ],
      onProgress: (track) => console.log(track.index, track.currentTime, track.duration),
      onEnded: () => console.log('Show over'),
    });
    
    // Call from a click handler. Browsers only allow audio after a user gesture.
    playButton.addEventListener('click', () => player.togglePlayPause());

    gapless.js preloads and decodes upcoming tracks in the background and starts each one on the exact sample the previous one ends. Bring your own UI. Read the full docs →