A gapless JavaScript audio player using HTML5 and WebAudio.
PROBLEM: There are 2 modern APIs for playing audio through the web, and both of them have problems:
SOLUTION: Use both!
The following sites utilize Gapless 5. If you'd like to be featured here, please contact the repo owner or start a new issue!
Gapless 5 Demonstration Page: Utilizes key mappings for cueing and other features.
THE402: An electronic music looping experience.
Woodhelm: A live ambience mixing website.
Bernardo.fm: Featuring electronic and hip-hop artists.
This is Nerdpop: Interactive listening page for Zen Finger Painting's indie pop album.
loadLimit
under Options)NOTE for Boostrap users: Bootstrap's CSS will mess up the optional built-in UI. If you don't need Bootstrap in its entirety, try using Twitter customize to get just the subset of rules you need.
Install the npm package:
$ npm install @regosen/gapless-5
Import Gapless5
from the module:
const { Gapless5 } = require('@regosen/gapless-5');
A. If not using built-in UI, just add and reference Gapless5.js
from your HTML head.
<script src="https://github.com/regosen/Gapless-5/raw/main/gapless5.js" language="JavaScript" type="text/javascript"></script>
B. If using the built-in UI, add and reference Gapless5.js
and Gapless5.css
. Also, create a <div>
or <span>
element where you want the player to appear. Give it a particular id.
<link href="https://github.com/regosen/Gapless-5/blob/main/gapless5.css" rel="stylesheet" type="text/css" />
<script src="https://github.com/regosen/Gapless-5/raw/main/gapless5.js" language="JavaScript" type="text/javascript"></script>
<!-- then in body: -->
<div id="gapless5-player-id" />
Gapless5
object with an optional parameter object
guiId
under options.addTrack()
crossfade
under options.
insertTrack()
, removeTrack()
, and more.mapKeys()
or options in constructor.const player = new Gapless5({ guiId: 'gapless5-player-id' });
// You can add tracks by relative or absolute URL:
player.addTrack('audio/song1.mp3');
player.addTrack('https://my-audio-site.org/song2.m4a');
// You can also let the user upload tracks from a file loader like this:
const files = document.getElementById('my-file-input').files;
files.forEach(file => {
player.addTrack(URL.createObjectURL(file)); // this creates a 'blob://' URL
});
player.play();
If you want the user to upload tracks from a file loader, here's an example of that:
<form>
<input type="file" id="my-file-input" accept="audio/*">
</form>
These can be passed into a Gapless5
constructor, or (with the exception of tracks
and guiId
) set later on the object.
div
or span
) where you want the player to appear.random
for a random indexCrossfadeShape.None
(default, overlaps both tracks at full volume)CrossfadeShape.Linear
CrossfadeShape.EqualPower
(curved, louder than linear)LogLevel.Info
)LogLevel.Debug
for more verbose loggingExample:
const player = new Gapless5({
tracks: ['loop1.mp3', 'loop2.mp3'],
loop: true,
loadLimit: 2,
mapKeys: {prev: 'a', playpause: 's', stop: 'd', next: 'f'},
});
NOTE 1: if you need audio to keep playing on iOS with Safari in the background, set useWebAudio
to false
.
NOTE 2: if you use loadLimit
with useWebAudio
set to false
, Safari on iOS may fail to play subsequent tracks. This is due to user interaction requirements, and JS console will show a warning if this happens.
You can call these functions on Gapless5
objects.
audioPath
: path to audio file(s) or blob URL(s), see examples aboveindex
audioPath
: same as in addTrackindex
audioPath
: same as in addTrackindexOrPath
can be the numerical index, or audio pathgotoPath
, but waits for current track to finish firstindexOrPath
can be the numerical index, or audio pathplaybackRate
option)jsonMapping
maps an action to a key, see example code belowAll actions can be mapped to keys via mapKeys
.
These correspond to built-in UI buttons
These do not correspond to built-in UI buttons
Examples:
player.mapKeys({cue: '7', stop: '8', next: '9'});
player.play();
player.pause();
// indexes start at 0
player.replaceTrack(0, 'audio/song1_alt.flac');
player.insertTrack(1, 'audio/transition.wav');
player.gotoTrack(1);
player.gotoTrack('audio/song1_alt.flac'); // can also goto track by path
player.removeTrack(2);
player.removeTrack('audio/transition.wav'); // can also remove track by path
player.removeAllTracks();
You can set these on a Gapless5
object. All callbacks include the affected track's audio path except where indicated.
// audio position has changed
ontimeupdate = (current_track_time: number, current_track_index: number) => void
// play requested by user
onplayrequest = (track_path: string) => void
// play actually starts
onplay = (track_path: string) => void
// play is paused
onpause = (track_path: string) => void
// play is stopped
onstop = (track_path: string) => void
// prev track, where:
// from_track = track that we're switching from
// to_track = track that we're switching to
onprev = (from_track: string, to_track: string) => void
// next track, where:
// from_track = track that we're switching from
// to_track = track that we're switching to
onnext = (from_track: string, to_track: string) => void
// loading started
onloadstart = (track_path: string) => void
// loading completed
// fully_loaded = true for WebAudio data, false for HTML5 Audio data
// NOTE: this triggers twice per track when both WebAudio and HTML5 are enabled
onload = (track_path: string, fully_loaded: boolean) => void
// track unloaded (to save memory)
onunload = (track_path: string) => void
// track failed to load or play
onerror = (track_path: string, error?: Error | string) => void
// track finished playing
onfinishedtrack = (track_path: string) => void
// entire playlist finished playing
onfinishedall = () => void
Example:
function nextCallback(from_track, to_track) {
console.log(`User skipped to next track (from ${from_track} to ${to_track})`);
}
const player = new Gapless5({guiId: 'gapless5-player-id', tracks: ['track1.mp3', 'track2.mp3']});
player.onnext = nextCallback;
player.onplay = function (track_path) { console.log(`Now playing ${track_path}`); };
While Gapless provides its own GUI, you can also customize it in CSS, or even create your own spans of text controlled by Gapless5.
.g5positionbar
by class will affect the entire text above all Gapless5 players on your page#g5positionbar-[ID]
by id (where [ID]
of the guiId you provided) will also customize the entire text for the current player#g5position-[ID]
will be set to the current position (e.g. "04:04.95") #g5duration-[ID]
will be set to the track's duration#g5index-[ID]
will be set to the track's index in the playlist#g5numtracks-[ID]
will be set to the number of tracks in the playlist#g5trackname-[ID]
will be set to the current track name (filename without extension)Example: in CSS, hide the built-in gapless5 text:
#g5positionbar-MyID {
display: none;
}
and then create your own elements to be controlled by Gapless5:
<p>
Now Playing: <span id="#g5trackname-MyID"><span>
(<span id="#g5position-MyID"><span>)
</p>
See an example of customized player text here.
Licensed under the MIT License.