Skip to content
DocumentationAPI Reference

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.

ScopeAccess
user-read-playback-stateRead player state and devices
user-modify-playback-stateControl playback
user-read-currently-playingRead currently playing track

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}
`);
}
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}`);
}

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 to a specific device and start playing
const { 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,
});
}

// Resume playback
await client.me.player.startPlayback();
// Play a specific album
await client.me.player.startPlayback({
context_uri: "spotify:album:5ht7ItJgpBH7W6vJ5BqpPr",
});
// Play a playlist starting from track 5
await client.me.player.startPlayback({
context_uri: "spotify:playlist:37i9dQZF1DXcBWIGoYBM5M",
offset: { position: 4 }, // 0-indexed
});
// Play specific tracks
await client.me.player.startPlayback({
uris: [
"spotify:track:4iV5W9uYEdYUVa79Axb7Rh",
"spotify:track:1301WleyT98MSxVHPZCA6M",
],
});
// Play a track starting at 30 seconds
await client.me.player.startPlayback({
uris: ["spotify:track:4iV5W9uYEdYUVa79Axb7Rh"],
position_ms: 30000,
});
// Play on a specific device
await client.me.player.startPlayback(
{ context_uri: "spotify:album:5ht7ItJgpBH7W6vJ5BqpPr" },
{ device_id: "deviceId" },
);
await client.me.player.pausePlayback();
// Pause on a specific device
await client.me.player.pausePlayback({ device_id: "deviceId" });
// Skip to next track
await client.me.player.skipNext();
// Skip to previous track
await client.me.player.skipPrevious();
// Seek to 1 minute 30 seconds
await client.me.player.seekToPosition({ position_ms: 90000 });

// Set volume to 50%
await client.me.player.setVolume({ volume_percent: 50 });
// Enable shuffle
await client.me.player.toggleShuffle({ state: true });
// Disable shuffle
await client.me.player.toggleShuffle({ state: false });
// Repeat current track
await client.me.player.setRepeatMode({ state: "track" });
// Repeat playlist/album
await client.me.player.setRepeatMode({ state: "context" });
// Turn off repeat
await client.me.player.setRepeatMode({ state: "off" });

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 a track to the queue
await client.me.player.addToQueue({
uri: "spotify:track:4iV5W9uYEdYUVa79Axb7Rh",
});

Here’s a comprehensive example for controlling playback using the SDK:

import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
// Toggle play/pause
async 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 content
async 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 info
async 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 examples
await toggle();
await playPlaylist("37i9dQZF1DXcBWIGoYBM5M");
await client.me.player.skipNext();
await client.me.player.addToQueue({
uri: "spotify:track:4iV5W9uYEdYUVa79Axb7Rh",
});
// Poll for progress updates
setInterval(async () => {
const progress = await getProgress();
if (progress) {
console.log(`${progress.track.name}: ${progress.percent.toFixed(1)}%`);
}
}, 1000);

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,
};
}
}
// Usage
const client = new Spotted({ bearerToken: userAccessToken });
const remote = new SpotifyRemote(client);
// Subscribe to state changes
remote.subscribe((state) => {
const display = remote.getDisplayState();
console.log("Player state updated:", display);
});
// Initial load
await remote.refresh();
// Control playback
await remote.togglePlay();
await remote.skip("next");
await remote.seekPercent(50);
await remote.changeVolume(10);

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;
}
}
// Usage
await safePlayerAction(() => client.me.player.startPlayback());