
Quick Start
1. Simple audio playlist (30 seconds)
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="vidply.css">
</head>
<body>
<div id="player"></div>
<script type="module">
import { Player, PlaylistManager } from 'vidply';
// 1. Create player
const player = new Player('#player');
// 2. Create playlist
const playlist = new PlaylistManager(player, {
autoAdvance: true,
showPanel: true
});
// 3. Load tracks
playlist.loadPlaylist([
{ src: 'song1.mp3', type: 'audio/mp3', title: 'Song 1', artist: 'Artist 1' },
{ src: 'song2.mp3', type: 'audio/mp3', title: 'Song 2', artist: 'Artist 2' },
{ src: 'song3.mp3', type: 'audio/mp3', title: 'Song 3', artist: 'Artist 3' }
]);
</script>
</body>
</html>
2. Complete minimal example
import { Player, PlaylistManager } from 'vidply';
const player = new Player('#player');
const playlist = new PlaylistManager(player);
playlist.loadPlaylist([
{ src: 'a.mp3', type: 'audio/mp3', title: 'Track A' },
{ src: 'b.mp3', type: 'audio/mp3', title: 'Track B' }
]);
That’s it!
Functions
- Navigate to the previous/next track – Use dedicated buttons to skip between tracks
- Automatic playback – Automatically plays the next track as soon as a track ends
- Loop mode – Jumps back to the first track after the last track
- Track information display – Displays the current track number, track title and artist
- Visual playlist window – Interactive list of all tracks with thumbnails and a toggle button
- Highlighting of the active track – Visual indicator for the track currently being played
- Support for mixed media – Combine audio and video files in a single playlist
- Lazy loading – Media files are only loaded when played (configurable)
- Improved keyboard navigation – Full keyboard support with arrow keys, Page Up/Down, Home/End
- Support for screen readers – ARIA labels, live regions and boundary announcements
- WCAG compliant – Meets accessibility guidelines for keyboard navigation and screen readers
- Custom tracks – Support for subtitles, chapters and other text tracks per playlist item
Installation
The PlaylistManager is included in the VidPly package:
import { Player, PlaylistManager } from 'vidply';
Configuration options
PlaylistManager options
{
autoAdvance: true, // Automatically play next track when current ends
autoPlayFirst: true, // Auto-play first track on load (if false: load/select first track, but do not start playback)
loop: false, // Loop back to first track after last
showPanel: true // Show visual playlist panel
}
Structure of the Track object
{
src: 'path/to/media.mp3', // Required: Media URL
type: 'audio/mp3', // Required: MIME type
title: 'Track Title', // Optional: Track title
artist: 'Artist Name', // Optional: Artist name
duration: 180, // Optional: Duration in seconds
poster: 'path/to/thumbnail.jpg', // Optional: Thumbnail image
tracks: [ // Optional: Text tracks (captions, chapters)
{
src: 'captions.vtt',
kind: 'captions',
srclang: 'en',
label: 'English'
}
]
}
API methods
loadPlaylist(items)
Loads an array of track elements into the playlist.
- If
autoPlayFirst: true(default): The first track is played. - If
autoPlayFirst: false: The first track is selected and loaded (so that posters/UI/functions can be initialised), but playback is not started.
playlist.loadPlaylist([
{ src: 'track1.mp3', type: 'audio/mp3', title: 'Track 1' },
{ src: 'track2.mp3', type: 'audio/mp3', title: 'Track 2' }
]);
Parameter:
items(Array): Array of track objects (see track object structure above)
addItem(item)
Adds a single track to the playlist.
playlist.addItem({
src: 'track3.mp3',
type: 'audio/mp3',
title: 'Track 3'
});
Parameters:
item(Object): Track object (see track object structure above)
play(index)
Plays a specific track based on its index (0-based).
playlist.play(2); // Play third track (0-based index)
Parameters:
index(Number): Zero-based index of the track to be played
next()
Play the next track.
playlist.next();
previous()
Play the previous track.
playlist.previous();
hasNext()
Checks whether there is a next track.
if (playlist.hasNext()) {
playlist.next();
}
hasPrevious()
Checks whether there is a previous track.
if (playlist.hasPrevious()) {
playlist.previous();
}
getCurrentTrack()
Retrieves the current track object.
const track = playlist.getCurrentTrack();
console.log(track.title);
togglePanel()
Toggles the visibility of the playlist window.
playlist.togglePanel();
Examples of programmatic control
// Navigate
playlist.next(); // Go to next track
playlist.previous(); // Go to previous track
playlist.play(2); // Play track at index 2
// Check state
if (playlist.hasNext()) {
console.log('Has next track');
}
// Get current
const track = playlist.getCurrentTrack();
console.log(track.title);
// Listen to changes
player.on('playlisttrackchange', (e) => {
console.log('Now playing:', e.item.title);
});
Events
Monitoring playlist events via the player:
// Track change event
player.on('playlisttrackchange', (e) => {
console.log('Now playing:', e.item.title);
console.log('Track index:', e.index);
});
Examples
Playlist with subtitles
const tracks = [
{
src: 'song.mp3',
type: 'audio/mp3',
title: 'My Song',
artist: 'My Artist',
tracks: [
{ src: 'captions-en.vtt', kind: 'captions', srclang: 'en', label: 'English' },
{ src: 'captions-es.vtt', kind: 'captions', srclang: 'es', label: 'Español' }
]
}
];
playlist.loadPlaylist(tracks);
Dynamic playlist
// Start with empty playlist
const playlist = new PlaylistManager(player);
// Add tracks dynamically
playlist.addItem({
src: 'new-song.mp3',
type: 'audio/mp3',
title: 'New Song'
});
// Or reload entire playlist
playlist.loadPlaylist(newTracksArray);
Video playlist
// Create video player (note: mediaType: 'video')
const player = new Player('#video-player', {
mediaType: 'video'
});
const playlist = new PlaylistManager(player, {
autoAdvance: true,
showPanel: true
});
const videoTracks = [
{
src: 'video1.mp4',
type: 'video/mp4',
title: 'Episode 1',
poster: 'thumbnail1.jpg',
tracks: [
{ src: 'video1-captions.vtt', kind: 'captions', srclang: 'en', label: 'English' },
{ src: 'video1-chapters.vtt', kind: 'chapters', srclang: 'en', label: 'Chapters' }
]
},
{
src: 'video2.mp4',
type: 'video/mp4',
title: 'Episode 2',
poster: 'thumbnail2.jpg'
}
];
playlist.loadPlaylist(videoTracks);
Hide playlist panel
// Create without panel
const playlist = new PlaylistManager(player, {
showPanel: false
});
// Or toggle it
playlist.togglePanel();
Complete example with all features
import { Player, PlaylistManager } from 'vidply';
// Create player
const player = new Player('#audio-player', {
autoplay: false,
controls: true,
preload: 'metadata'
});
// Create playlist
const playlist = new PlaylistManager(player, {
autoAdvance: true,
loop: false,
showPanel: true
});
// Load tracks with captions and chapters
const tracks = [
{
src: 'media/song1.mp3',
type: 'audio/mp3',
title: 'Summer Vibes',
artist: 'The Acoustic Project',
duration: 245,
poster: 'media/album-art-1.jpg',
tracks: [
{
src: 'media/song1-captions-en.vtt',
kind: 'captions',
srclang: 'en',
label: 'English'
},
{
src: 'media/song1-chapters.vtt',
kind: 'chapters',
srclang: 'en',
label: 'Chapters'
}
]
},
{
src: 'media/song2.mp3',
type: 'audio/mp3',
title: 'Midnight Jazz',
artist: 'Blue Note Ensemble',
duration: 198,
poster: 'media/album-art-2.jpg'
}
];
playlist.loadPlaylist(tracks);
// Listen for track changes
player.on('playlisttrackchange', (e) => {
console.log(`Now playing: ${e.item.title} by ${e.item.artist}`);
// Optional: Update document title
document.title = `${e.item.title} - ${e.item.artist}`;
// Optional: Update media session API
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: e.item.title,
artist: e.item.artist,
artwork: [{ src: e.item.poster }]
});
}
});
// Optional: Add keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
return; // Don't interfere with form inputs
}
switch(e.key) {
case 'ArrowLeft':
if (e.shiftKey) {
playlist.previous();
e.preventDefault();
}
break;
case 'ArrowRight':
if (e.shiftKey) {
playlist.next();
e.preventDefault();
}
break;
case 'p':
playlist.togglePanel();
break;
}
});
Behaviour in full-screen mode
In full-screen mode, the playlist transforms into a horizontal, swipeable carousel (similar to YouTube):
- Automatic show/hide: Displayed when playback is paused or has not yet started, and hidden when playback is in progress
- Horizontal layout: Cards are displayed side by side with scroll-snap navigation
- Responsive: On desktop, full cards (280px) are displayed with thumbnails, title and artist
- Mobile: Compact view with thumbnails only in portrait orientation (<768px); text is displayed if no thumbnail is available
- Touch-friendly: Swipeable with smooth horizontal scrolling
- Position: Located above the controls with a semi-transparent background
- Accessibility: Menus are dynamically moved to the container level to appear above the playlist
All menus (chapters, quality, subtitles, etc.) remain accessible in full-screen mode via dynamic DOM repositioning, ensuring WCAG compliance.
UI components
Display of title information
Displayed above the controls and contains:
- Track number (e.g. “3 / 10”)
- Track
- Artist name (if provided)
Playlist area
A scrollable list below the player that displays the following:
- Thumbnails of the tracks (where available)
- Track numbers
- Track names and artists
- Track duration (if available)
- Display of the current track
Navigation buttons
When a playlist is active:
- Back button – Replaces the rewind button
- ‘Next’ button – Replaces the fast-forward button
- The buttons are disabled at the edges of the playlist (unless the repeat function is enabled)
Design
Customise the appearance of the playlist using CSS:
/* Track info display */
.vidply-track-info {
background: your-gradient;
padding: 20px;
}
.vidply-track-title {
font-size: 18px;
color: #fff;
}
.vidply-track-artist {
color: rgba(255, 255, 255, 0.8);
}
/* Playlist panel */
.vidply-playlist-panel {
background: rgba(20, 20, 30, 0.95);
max-height: 400px;
}
.vidply-playlist-item-active {
background: rgba(59, 130, 246, 0.2);
border-left-color: #3b82f6;
}
/* Custom hover effects */
.vidply-playlist-item:hover {
background: rgba(255, 255, 255, 0.1);
}
/* Active playlist item */
.vidply-playlist-item-active {
background: linear-gradient(90deg, #667eea, #764ba2);
border-left-color: #fff;
}
Keyboard navigation and accessibility
Integrated playlist navigation
The playlist window offers comprehensive support for keyboard navigation:
- ↑ Up arrow – Jump to the previous track in the list
- ↓ Down arrow – Jump to the next track in the list
- Page Up – Jump up 5 tracks
- Down arrow – Jump down 5 tracks
- Home – Jump to the first track
- End – Skip to the last track
- Enter / Space bar – Play the selected track
- Tab – Navigate to the playlist area and switch between tracks (roving tabindex pattern)
Playlist toggle button
A button to show and hide the playlist is automatically added to the control bar when a PlaylistManager is active. With this button, users can:
- Toggle the visibility of the playlist window
- Access the playlist via keyboard navigation (Tab to the toggle, Enter to toggle)
- Correct ARIA attributes for screen readers (
aria-expanded,aria-pressed,aria-controls)
Support for screen readers
The playlist offers comprehensive support for screen readers:
- Live announcements whilst navigating (e.g. “End of playlist. 5 of 5.”)
- Announcements regarding boundaries (start/end of the playlist)
- Announcements regarding track position (e.g. “Track 3 of 10”)
- Status announcements (currently playing, not playing)
- Descriptive labels for all interactive elements
Global keyboard shortcuts (optional)
You can add global keyboard shortcuts for navigating the playlist:
document.addEventListener('keydown', (e) => {
// Don't interfere with form inputs
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
return;
}
if (e.key === 'ArrowRight' && e.shiftKey) {
playlist.next();
e.preventDefault();
}
if (e.key === 'ArrowLeft' && e.shiftKey) {
playlist.previous();
e.preventDefault();
}
});
Accessibility methods
// Toggle playlist panel visibility
playlist.togglePanel();
// Show playlist panel
playlist.showPanel();
// Hide playlist panel
playlist.hidePanel();
// Check if panel is visible
const isVisible = playlist.isPanelVisible;
Common use cases
Music player
const musicPlayer = new PlaylistManager(player, {
autoAdvance: true,
loop: true,
showPanel: true
});
Ideal for:
- Album playback
- An artist’s discography
- Music compilations
Podcast playlists
const podcast = new PlaylistManager(player, {
autoAdvance: false, // Manual navigation
loop: false,
showPanel: true
});
Ideal for:
- Podcast series
- Episode collections
- Interview playlists
Video series (Netflix-style)
const series = new PlaylistManager(player, {
autoAdvance: true, // Binge watching
loop: false,
showPanel: true
});
Ideal for:
- Episodes of TV series
- Video courses
- Tutorial series
- Conference presentations
Streaming playlists (HLS & DASH)
playlist.loadPlaylist([
{ src: 'https://example.com/video1/manifest.mpd', title: 'DASH Stream' },
{ src: 'https://example.com/video2/master.m3u8', title: 'HLS Stream' },
{ src: 'fallback.mp4', type: 'video/mp4', title: 'MP4 Fallback' }
]);
VidPly automatically detects the renderer for each element in the playlist based on the file extension of the source URL (.mpd for DASH, .m3u8 for HLS, etc.).
Mixed-media playlist
const mixedPlaylist = new PlaylistManager(player, {
autoAdvance: true,
showPanel: true
});
mixedPlaylist.loadPlaylist([
{ src: 'intro.mp4', type: 'video/mp4', title: 'Introduction Video' },
{ src: 'episode1.mp3', type: 'audio/mp3', title: 'Episode 1 Audio' },
{ src: 'episode2.mp4', type: 'video/mp4', title: 'Episode 2 Video' },
{ src: 'bonus.mp3', type: 'audio/mp3', title: 'Bonus Content' }
]);
Ideal for:
- Courses with mixed content (video lectures + audio supplements)
- Multimedia presentations
- Content with video and audio episodes
- Educational content that combines different media types
Audiobook chapters
const audiobook = new PlaylistManager(player, {
autoAdvance: true,
loop: false,
showPanel: true
});
Ideal for:
- Chapter navigation
- Multi-part stories
- Educational content in audio format
Best practice
- Avoid premature loading of network data: Use
deferLoad: true(and optionallypreload: 'none') to prevent downloads from starting during initialisation - Preload metadata: Set
preload: 'metadata'to load the track duration without downloading the full files - Provide thumbnails: Add poster images for better visual representation
- Include duration: Calculate the duration in advance for a better user experience
- Use consistent naming: Keep the properties of the track objects in your playlist consistent
- Handle loading states: On
loadstartandcanplayevents for loading indicators - Accessibility: Ensure that track titles and artists are meaningful for screen readers
Troubleshooting
Tracks do not play automatically
Ensure autoAdvance: true is set in the playlist options and that the media item triggers the ended triggers the event.
‘Back’/‘Next’ buttons are not displayed
The buttons are only displayed if an PlaylistManager instance is attached to the player. Ensure that you create the Playlist Manager before the controls are rendered, or call player.renderControls() after creating the Playlist Manager.
Playlist panel not visible
Check whether showPanel: true is set in the playlist options. The panel is inserted in the DOM behind the player element.
Subtitles do not switch between tracks
Ensure that the tracks array is included in every playlist element that contains subtitles. The subtitles are reloaded when switching between playlist elements.
Browser support
The playlist feature works in all modern browsers that support the following:
- ES6 modules
- HTML5 media elements
- WebVTT (for subtitles/chapters)
Tested in:
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Opera 76+
Demos
View all demos:
- Audio playlist: demo/playlist-audio.html
- Video playlist: demo/playlist-video.html
- Mixed media playlist: demo/playlist-mixed.html
Implementation details
Added files
src/features/PlaylistManager.js- Core playlist functionalitydemo/playlist-audio.html- Working audio playlist demo with 5 tracksdemo/playlist-video.html- Working demo of a video playlist with 3 videos
Modified files
src/controls/ControlBar.js- Previous/Next buttons addedsrc/styles/vidply.css- Playlist styles addedsrc/index.js- PlaylistManager exporteddemo/demo.html- Links to playlist demos added
Built with Vanilla JavaScript