npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@scarlett-player/playlist

v1.25.0

Published

Playlist Plugin for Scarlett Player - Queue management, shuffle, repeat, and gapless playback

Readme

@scarlett-player/playlist

Playlist plugin for Scarlett Player. Queue management (add, insert, remove, move, clear), shuffle with a Fisher-Yates order, repeat modes, auto-advance when a track ends, optional localStorage persistence, and control-bar buttons for previous, next and a queue panel when @scarlett-player/ui is installed.

Docs: scarlettplayer.com/documentation · For AI coding agents: llms.txt (index) and llms-full.txt (every guide as one Markdown file)

Installation

pnpm add @scarlett-player/playlist @scarlett-player/core

@scarlett-player/core is a required peer dependency. @scarlett-player/ui is an optional peer: the control-bar controls register only when it is present, and everything else works without it.

Usage

This matches the audio example in the root README.

import { createPlayer } from '@scarlett-player/core';
import { createNativePlugin } from '@scarlett-player/native';
import { createAudioUIPlugin } from '@scarlett-player/audio-ui';
import { createPlaylistPlugin, type IPlaylistPlugin } from '@scarlett-player/playlist';
import { createMediaSessionPlugin } from '@scarlett-player/media-session';

const player = await createPlayer({
  container: document.getElementById('audio-player'),
  plugins: [
    createNativePlugin(),
    createAudioUIPlugin({ layout: 'full' }),
    createPlaylistPlugin({
      tracks: [
        { id: '1', src: '/track1.mp3', title: 'Track 1', artist: 'Artist', artwork: '/art1.jpg' },
        { id: '2', src: '/track2.mp3', title: 'Track 2', artist: 'Artist', artwork: '/art2.jpg' },
      ],
    }),
    createMediaSessionPlugin(),
  ],
});

// Start the first track through the playlist, not through `src` or
// player.load(). The playlist owns the current index: play() sets it, writes
// the track's title and artwork into state, and emits `media:load-request`,
// which the player loads and plays. Loading a source behind the playlist's
// back leaves its index pointing at nothing, so next/previous and
// auto-advance start from the wrong place.
const playlist = player.getPlugin<IPlaylistPlugin>('playlist');
playlist?.play(0);

If you do pass src to createPlayer() for the first track, set initialIndex: 0 so the playlist knows which track is already loaded.

Configuration

| Option | Type | Default | Description | |---|---|---|---| | tracks | PlaylistTrack[] | [] | Initial queue. Tracks without an id get a generated one | | autoAdvance | boolean | true | Select the next track on playback:ended | | advanceDelay | number | 0 | Milliseconds to wait before auto-advancing | | autoLoad | boolean | true | Emit media:load-request whenever the current track changes, so the player loads it. A manual selection carries autoplay: true; an auto-advance carries whether the viewer had playback running, so a paused playlist stays paused, including on a player constructed with autoplay: true. Set to false to load from playlist:change yourself | | shuffle | boolean | false | Initial shuffle state | | repeat | 'none' \| 'one' \| 'all' | 'none' | Initial repeat mode | | initialIndex | number | -1 | Index of the track already loaded by the player. -1 means no track is active. Out-of-range values fall back to -1 | | persist | boolean | false | Save tracks, index, shuffle and repeat to localStorage and restore them on init | | persistKey | string | 'scarlett-playlist' | localStorage key used by persist | | preloadNext | boolean | true | Warm the next track when the current one starts: a detached media element with preload="metadata", so DNS, TLS and the container header are done before the viewer gets there. Nothing is downloaded ahead beyond that, and the end of a non-repeating playlist warms nothing |

A PlaylistTrack is { id, src, videoId?, title?, artist?, album?, artwork?, duration?, type?, mimeType?, metadata? } plus any extra properties you want to carry. videoId is what @scarlett-player/analytics reports for the track's view (each track that loads gets its own view); without it analytics reports its configured videoId, never id. type defaults to 'audio' in player state when omitted.

Plugin API

Registered under the id playlist; retrieve it with player.getPlugin<IPlaylistPlugin>('playlist').

| Method | Description | |---|---| | add(track \| track[]) | Append to the queue | | insert(index, track) | Insert at a position (clamped to the queue length) | | remove(idOrIndex) | Remove by id or index. Removing the current track selects the track that slides into its slot | | move(fromIndex, toIndex) | Reorder | | clear() | Empty the queue and reset the index to -1 | | play(idOrIndex?) | Select a track. With no argument, resumes the current track or starts the first | | next() / previous() | Step through the queue honouring shuffle and repeat. previous() restarts the current track when more than 3 seconds in | | toggleShuffle() / setShuffle(enabled) | Shuffle control. The current track stays first in the new order | | cycleRepeat() / setRepeat(mode) | Repeat control. cycleRepeat() goes none, all, one | | getState() | { tracks, currentIndex, currentTrack, shuffle, repeat, shuffleOrder, hasNext, hasPrevious } | | getTracks() / getCurrentTrack() / getTrack(id) | Read the queue |

With the player focused, the N and P keys call next() and previous(). Keystrokes inside inputs or with a modifier held are left alone.

Events

All payload types live in PlayerEventMap in @scarlett-player/core.

| Event | Payload | When | |---|---|---| | playlist:change | { track, index } | The current track changed. track is null after clear() | | playlist:add | { track, index } | add() (once per track) or insert() | | playlist:remove | { track, index } | remove() | | playlist:reorder | { tracks } | move() | | playlist:clear | void | clear() | | playlist:shuffle | { enabled } | Shuffle changed | | playlist:repeat | { mode } | Repeat mode changed | | playlist:ended | void | The last track ended with nothing to advance to |

On every track change the plugin also writes title, poster (from artwork) and mediaType into player state, clearing title and poster when the track has none so a previous track's values never leak.

Control-bar controls

When @scarlett-player/ui is installed, three controls are registered. Nothing is placed until you list them in the UI layout:

uiPlugin({ controls: ['playlist-previous', 'play', 'playlist-next', 'progress', 'spacer', 'playlist', 'fullscreen'] })

| Control id | Renders | |---|---| | playlist-previous / playlist-next | Skip buttons, disabled at the ends of the queue | | playlist | A button that opens the queue as a list |

All three hide themselves when the queue has one track or fewer. The classes PlaylistSkipButton, PlaylistPanel and the PLAYLIST_ICONS map are exported for custom layouts.

Styling

A stylesheet is injected once per document (style element id sp-playlist-styles). Override these classes to restyle: sp-playlist-skip (with --previous / --next and the [disabled] state), sp-playlist, sp-playlist--open, sp-playlist__button, sp-playlist__panel, sp-playlist__item, sp-playlist__item--active, sp-playlist__position, sp-playlist__text, sp-playlist__title and sp-playlist__artist. No CSS custom properties are used.

License

MIT