Skip to content

Playback & filters

Accepts either a search string or a pre-resolved source:

// String query — searches and plays the first result
await player.play("lofi hip hop", { guildId, voiceId });
// Pre-resolved Track
await player.play(track, { guildId, voiceId });
// Array of Tracks
await player.play([track1, track2], { guildId, voiceId });
// Playlist
await player.play(playlist, { guildId, voiceId });

When given a string, it searches using the queue’s node if a queue exists, or the best available node otherwise. If the result is of type query, it takes the first result.

If a queue already exists and is stopped, play() adds the track and calls resume(). If it’s already playing, the track is added to the end of the queue.

Range is 0–1000. Default is 100. Values above 100 amplify and may clip.

await queue.setVolume(80);
await queue.setVolume(0); // mute
await queue.setVolume(150); // amplify (may clip)

Every method exists on both Player (requires a guildId) and Queue (operates on itself). They’re identical in behaviour — use whichever is more convenient.

// Via Player
await player.pause(guildId);
await player.resume(guildId);
await player.stop(guildId);
await player.seek(guildId, 30_000); // ms from start
// Via Queue
const queue = player.getQueue(guildId)!;
await queue.pause();
await queue.resume();
await queue.stop();
await queue.seek(30_000);

stop() vs destroy()stop() halts playback and clears Lavalink’s player track but keeps the queue intact locally.

resume() behaviour — if queue.stopped is true, resumes by calling jump(0) to restart the current track. Otherwise unpauses.

seek() behaviour — seeks to the given position in ms. Also unpauses if paused. Throws if the current track is not seekable or if ms exceeds the current track’s duration.

await queue.next(); // advance one track
await queue.previous(); // go back one track
await queue.jump(3); // jump to index 3 in the current list
await queue.jump(0); // restart the current track
await queue.jump(-1); // jump to the most recent previous track
await queue.jump(-2); // jump two tracks back

jump() always starts playback immediately and triggers trackStart. Throws if the queue is empty or the index is out of range.

next() with repeatMode: 'queue' — when the current list is exhausted and there are previous tracks, next() moves the oldest previous track to the end of the current list and jumps to it, cycling the queue.

next() with autoplay — when the current list is exhausted and autoplay is on, next() calls addRelated() and jumps to the first related track if any are returned.

queue.remove(1); // remove by index → Track | undefined
queue.remove([1, 3, 5]); // remove multiple → Track[]
queue.remove(-1); // remove the most recent previous track

Index 0 (the current track) can only be removed when queue.stopped is true.

queue.clear(); // remove everything (keeps current if playing)
queue.clear("current"); // remove upcoming (+current if stopped)
queue.clear("previous"); // remove history only
queue.shuffle(); // shuffle current tracks (index 1+), no-op if fewer than 3
queue.shuffle(true); // pull all previous tracks into current, then shuffle
queue.setRepeatMode("none"); // play through once (default)
queue.setRepeatMode("track"); // loop the current track indefinitely
queue.setRepeatMode("queue"); // loop the entire queue
queue.setAutoplay(true);
queue.setAutoplay(false);
// Add related tracks manually without enabling autoplay
await queue.addRelated();
await queue.addRelated(specificTrack);

Autoplay calls fetchRelatedTracks when the queue empties after a track finishes. The default returns [] — provide your own implementation to enable it:

const player = new Player({
async fetchRelatedTracks(queue, track) {
// track is the one that just finished
// DO NOT throw — return [] to stop gracefully
return [];
},
});

Each queue has a FilterManager at queue.filters. Every method sends an update to Lavalink immediately and returns the resulting state — no separate apply step.

// Nightcore — faster, higher pitch
await queue.filters.set("timescale", { speed: 1.3, pitch: 1.3 });
// Vaporwave — slower, lower pitch
await queue.filters.set("timescale", { speed: 0.8, pitch: 0.8 });
// 8D audio — rotating stereo field
await queue.filters.set("rotation", { rotationHz: 0.2 });
// Bass boost
await queue.filters.set("equalizer", [
{ band: 0, gain: 0.3 },
{ band: 1, gain: 0.2 },
{ band: 2, gain: 0.1 },
]);
// Karaoke — attenuate vocals
await queue.filters.set("karaoke", { level: 1.0, monoLevel: 1.0, filterBand: 220, filterWidth: 100 });
// Soft tremolo
await queue.filters.set("tremolo", { frequency: 4, depth: 0.3 });
KeyEffect
volumeOutput volume multiplier (0.0–5.0, where 1.0 = 100%). Separate from queue.setVolume() which uses 0–1000
equalizer15-band EQ. Each band: { band: 0–14, gain: -0.25–1.0 }
karaokeAttenuates a frequency band — typically reduces vocals
timescaleAdjust speed, pitch, and rate independently (all default to 1.0, must be > 0)
tremoloRapidly oscillate volume (frequency > 0, depth > 0 and ≤ 1)
vibratoRapidly oscillate pitch (frequency > 0 and ≤ 14, depth > 0 and ≤ 1)
rotationRotate audio around the stereo field — the 8D effect (rotationHz)
distortionWave-function distortion with sin/cos/tan offset and scale controls
channelMixBlend left and right channels (leftToLeft, leftToRight, rightToLeft, rightToRight, each 0–1)
lowPassSuppress high frequencies (smoothing > 1.0 — values ≤ 1.0 disable the filter)
pluginFiltersFilters from Lavalink server plugins
queue.filters.get("timescale"); // TimescaleFilter | null
queue.filters.has("rotation"); // boolean
// Remove specific filters — returns the updated Filters object
await queue.filters.remove("timescale", "rotation");
// Clear by type — returns the updated Filters object
await queue.filters.clear(); // remove everything
await queue.filters.clear("native"); // built-in filters only
await queue.filters.clear("plugin"); // plugin filters only
// Merge — shallow-merges into the current filter state, returns updated Filters
await queue.filters.merge({ volume: 1.5 });
// Override — replaces the entire filter state, returns updated Filters
await queue.filters.override({ timescale: { speed: 1.2 } });

Use merge when you want to layer effects on top of what’s already active. Use override when you want a clean slate.

set() returns the value that was just applied:

const ts = await queue.filters.set("timescale", { speed: 1.3, pitch: 1.3 });
// ts → { speed: 1.3, pitch: 1.3 }

Plugin filters live under pluginFilters in the filter object. Pass true as the third argument to set():

await queue.filters.set("myFilter", { intensity: 0.8 }, true); // isPlugin = true

Declare the type so it propagates everywhere:

declare module "discolink" {
interface CommonPluginFilters {
myFilter: { intensity: number };
}
}

Full type definitions: Filters


If local state drifts — most commonly after a node reconnect:

await queue.sync(); // pull state from Lavalink (default)
await queue.sync("remote"); // push local state to Lavalink
// Sync all queues on a node at once
await player.queues.sync("us-east", "local");
await player.queues.sync("us-east", "remote");

autoSync: true (the default) calls queues.sync(node, 'remote') automatically when a node reconnects without resuming its session.