Playback Control
Control Spotify playback across devices using the Web API
The Spotify Connect endpoints allow you to control playback on any of the user’s devices. You can play, pause, skip tracks, adjust volume, and manage the playback queue programmatically.
Required Scopes
Section titled “Required Scopes”| Scope | Access |
|---|---|
user-read-playback-state | Read player state and devices |
user-modify-playback-state | Control playback |
user-read-currently-playing | Read currently playing track |
Get Playback State
Section titled “Get Playback State”Current Playback
Section titled “Current Playback”import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
const state = await client.me.player.getState();
if (state) { console.log(` Device: ${state.device.name} Playing: ${state.is_playing} Track: ${state.item?.name} Artist: ${state.item?.artists[0]?.name} Progress: ${Math.floor(state.progress_ms / 1000)}s / ${Math.floor(state.item?.duration_ms / 1000)}s Volume: ${state.device.volume_percent}% Shuffle: ${state.shuffle_state} Repeat: ${state.repeat_state} `);}Currently Playing Track
Section titled “Currently Playing Track”const current = await client.me.player.getCurrentlyPlaying();
if (current?.item) { console.log(`Now playing: ${current.item.name}`); console.log(`Context: ${current.context?.type} - ${current.context?.uri}`);}Device Management
Section titled “Device Management”Get Available Devices
Section titled “Get Available Devices”const { devices } = await client.me.player.getDevices();
devices.forEach((device) => { console.log(` ${device.name} (${device.type}) ID: ${device.id} Active: ${device.is_active} Volume: ${device.volume_percent}% ${device.is_restricted ? "(Restricted)" : ""} `);});Transfer Playback to Device
Section titled “Transfer Playback to Device”// Transfer to a specific device and start playingconst { devices } = await client.me.player.getDevices();const speaker = devices.find((d) => d.type === "Speaker");
if (speaker) { await client.me.player.transferPlayback({ device_ids: [speaker.id], play: true, });}Playback Controls
Section titled “Playback Controls”// Resume playbackawait client.me.player.startPlayback();
// Play a specific albumawait client.me.player.startPlayback({ context_uri: "spotify:album:5ht7ItJgpBH7W6vJ5BqpPr",});
// Play a playlist starting from track 5await client.me.player.startPlayback({ context_uri: "spotify:playlist:37i9dQZF1DXcBWIGoYBM5M", offset: { position: 4 }, // 0-indexed});
// Play specific tracksawait client.me.player.startPlayback({ uris: [ "spotify:track:4iV5W9uYEdYUVa79Axb7Rh", "spotify:track:1301WleyT98MSxVHPZCA6M", ],});
// Play a track starting at 30 secondsawait client.me.player.startPlayback({ uris: ["spotify:track:4iV5W9uYEdYUVa79Axb7Rh"], position_ms: 30000,});
// Play on a specific deviceawait client.me.player.startPlayback( { context_uri: "spotify:album:5ht7ItJgpBH7W6vJ5BqpPr" }, { device_id: "deviceId" },);await client.me.player.pausePlayback();
// Pause on a specific deviceawait client.me.player.pausePlayback({ device_id: "deviceId" });Skip to Next/Previous
Section titled “Skip to Next/Previous”// Skip to next trackawait client.me.player.skipNext();
// Skip to previous trackawait client.me.player.skipPrevious();Seek to Position
Section titled “Seek to Position”// Seek to 1 minute 30 secondsawait client.me.player.seekToPosition({ position_ms: 90000 });Volume and Playback Modes
Section titled “Volume and Playback Modes”Set Volume
Section titled “Set Volume”// Set volume to 50%await client.me.player.setVolume({ volume_percent: 50 });Toggle Shuffle
Section titled “Toggle Shuffle”// Enable shuffleawait client.me.player.toggleShuffle({ state: true });
// Disable shuffleawait client.me.player.toggleShuffle({ state: false });Set Repeat Mode
Section titled “Set Repeat Mode”// Repeat current trackawait client.me.player.setRepeatMode({ state: "track" });
// Repeat playlist/albumawait client.me.player.setRepeatMode({ state: "context" });
// Turn off repeatawait client.me.player.setRepeatMode({ state: "off" });Queue Management
Section titled “Queue Management”Get Current Queue
Section titled “Get Current Queue”const queue = await client.me.player.getQueue();
console.log(`Currently playing: ${queue.currently_playing?.name}`);console.log("Coming up:");queue.queue.slice(0, 5).forEach((track, i) => { console.log(`${i + 1}. ${track.name} - ${track.artists[0].name}`);});Add to Queue
Section titled “Add to Queue”// Add a track to the queueawait client.me.player.addToQueue({ uri: "spotify:track:4iV5W9uYEdYUVa79Axb7Rh",});Complete Player Controller
Section titled “Complete Player Controller”Here’s a comprehensive example for controlling playback using the SDK:
import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
// Toggle play/pauseasync function toggle() { const state = await client.me.player.getState(); if (state?.is_playing) { await client.me.player.pausePlayback(); } else { await client.me.player.startPlayback(); }}
// Play specific contentasync function playTrack(trackId) { await client.me.player.startPlayback({ uris: [`spotify:track:${trackId}`], });}
async function playAlbum(albumId, startTrack = 0) { await client.me.player.startPlayback({ context_uri: `spotify:album:${albumId}`, offset: { position: startTrack }, });}
async function playPlaylist(playlistId, startTrack = 0) { await client.me.player.startPlayback({ context_uri: `spotify:playlist:${playlistId}`, offset: { position: startTrack }, });}
// Get progress infoasync function getProgress() { const state = await client.me.player.getState(); if (!state?.item) return null;
return { track: state.item, progress: state.progress_ms, duration: state.item.duration_ms, percent: (state.progress_ms / state.item.duration_ms) * 100, isPlaying: state.is_playing, };}
// Usage examplesawait toggle();await playPlaylist("37i9dQZF1DXcBWIGoYBM5M");await client.me.player.skipNext();await client.me.player.addToQueue({ uri: "spotify:track:4iV5W9uYEdYUVa79Axb7Rh",});
// Poll for progress updatessetInterval(async () => { const progress = await getProgress(); if (progress) { console.log(`${progress.track.name}: ${progress.percent.toFixed(1)}%`); }}, 1000);Building a Remote Control UI
Section titled “Building a Remote Control UI”Example of building a player remote control using the SDK:
import Spotted from "spotted-ts";
class SpotifyRemote { constructor(client) { this.client = client; this.state = null; this.listeners = new Set(); }
subscribe(callback) { this.listeners.add(callback); return () => this.listeners.delete(callback); }
notify() { this.listeners.forEach((cb) => cb(this.state)); }
async refresh() { this.state = await this.client.me.player.getState(); this.notify(); return this.state; }
async togglePlay() { if (this.state?.is_playing) { await this.client.me.player.pausePlayback(); } else { await this.client.me.player.startPlayback(); } await this.refresh(); }
async skip(direction) { if (direction === "next") { await this.client.me.player.skipNext(); } else { await this.client.me.player.skipPrevious(); } // Wait a moment for Spotify to update await new Promise((r) => setTimeout(r, 300)); await this.refresh(); }
async seekPercent(percent) { if (!this.state?.item) return; const positionMs = Math.floor( this.state.item.duration_ms * (percent / 100), ); await this.client.me.player.seekToPosition({ position_ms: positionMs }); await this.refresh(); }
async changeVolume(delta) { if (!this.state?.device) return; const newVolume = Math.max( 0, Math.min(100, this.state.device.volume_percent + delta), ); await this.client.me.player.setVolume({ volume_percent: newVolume }); await this.refresh(); }
async toggleShuffle() { const newState = !this.state?.shuffle_state; await this.client.me.player.toggleShuffle({ state: newState }); await this.refresh(); }
async cycleRepeat() { const modes = ["off", "context", "track"]; const currentIndex = modes.indexOf(this.state?.repeat_state || "off"); const nextMode = modes[(currentIndex + 1) % modes.length]; await this.client.me.player.setRepeatMode({ state: nextMode }); await this.refresh(); }
async selectDevice(deviceId) { await this.client.me.player.transferPlayback({ device_ids: [deviceId], play: this.state?.is_playing, }); await this.refresh(); }
formatTime(ms) { const seconds = Math.floor(ms / 1000); const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, "0")}`; }
getDisplayState() { if (!this.state) { return { connected: false }; }
return { connected: true, isPlaying: this.state.is_playing, track: this.state.item ? { name: this.state.item.name, artist: this.state.item.artists.map((a) => a.name).join(", "), album: this.state.item.album.name, artwork: this.state.item.album.images[0]?.url, } : null, progress: { current: this.formatTime(this.state.progress_ms), total: this.formatTime(this.state.item?.duration_ms || 0), percent: this.state.item ? (this.state.progress_ms / this.state.item.duration_ms) * 100 : 0, }, device: this.state.device ? { name: this.state.device.name, type: this.state.device.type, volume: this.state.device.volume_percent, } : null, shuffle: this.state.shuffle_state, repeat: this.state.repeat_state, }; }}
// Usageconst client = new Spotted({ bearerToken: userAccessToken });const remote = new SpotifyRemote(client);
// Subscribe to state changesremote.subscribe((state) => { const display = remote.getDisplayState(); console.log("Player state updated:", display);});
// Initial loadawait remote.refresh();
// Control playbackawait remote.togglePlay();await remote.skip("next");await remote.seekPercent(50);await remote.changeVolume(10);Error Handling
Section titled “Error Handling”import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
async function safePlayerAction(action) { try { return await action(); } catch (error) { if (error.message.includes("No active device")) { // Try to find and activate a device const { devices } = await client.me.player.getDevices(); const available = devices.find((d) => !d.is_restricted);
if (available) { await client.me.player.transferPlayback({ device_ids: [available.id], }); return await action(); }
throw new Error("No available devices. Open Spotify on a device first."); }
if (error.message.includes("Premium")) { throw new Error("Playback control requires Spotify Premium."); }
throw error; }}
// Usageawait safePlayerAction(() => client.me.player.startPlayback());Next Steps
Section titled “Next Steps”- Search & Discovery - Find content to play
- Playlist Management - Create playlists to play
- Audio Analysis - Get audio features for tracks