Jump to content

Playlist function

The PlaylistManager allows you to create audio and video playlists with automatic track switching, navigation control and a visual playlist panel.

VidPly Logo

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

  1. Avoid premature loading of network data: Use deferLoad: true (and optionally preload: 'none') to prevent downloads from starting during initialisation
  2. Preload metadata: Set preload: 'metadata' to load the track duration without downloading the full files
  3. Provide thumbnails: Add poster images for better visual representation
  4. Include duration: Calculate the duration in advance for a better user experience
  5. Use consistent naming: Keep the properties of the track objects in your playlist consistent
  6. Handle loading states: On loadstart and canplay events for loading indicators
  7. 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:

Implementation details

Added files

  • src/features/PlaylistManager.js - Core playlist functionality
  • demo/playlist-audio.html - Working audio playlist demo with 5 tracks
  • demo/playlist-video.html - Working demo of a video playlist with 3 videos

Modified files

  • src/controls/ControlBar.js - Previous/Next buttons added
  • src/styles/vidply.css - Playlist styles added
  • src/index.js - PlaylistManager exported
  • demo/demo.html - Links to playlist demos added

Built with Vanilla JavaScript

Share page