Quick Start Guide
This guide will help you build a basic but fully functional music bot using Discord Player. By the end, you’ll have a bot that can play music from various sources!
Prerequisites
Before you begin, make sure you have:
Step 1: Initialize Your Bot
Create a new file called index.js and set up your Discord client:
const { Client , GatewayIntentBits } = require ( 'discord.js' );
const { Player } = require ( 'discord-player' );
const { DefaultExtractors } = require ( '@discord-player/extractor' );
const client = new Client ({
intents: [
GatewayIntentBits . Guilds ,
GatewayIntentBits . GuildMessages ,
GatewayIntentBits . GuildVoiceStates ,
GatewayIntentBits . MessageContent ,
],
});
The GuildVoiceStates intent is required for Discord Player to work. Without it, the bot won’t be able to connect to voice channels.
Step 2: Create the Player Instance
Create the main player instance and load extractors:
// Create the player instance - this is the entrypoint for discord-player
const player = new Player ( client );
// Load all default extractors
player . extractors . loadMulti ( DefaultExtractors ). then (() => {
console . log ( 'Extractors loaded successfully' );
});
The Player instance handles and keeps track of all queues and their components. You only need one player instance for your entire bot.
Step 3: Set Up Event Listeners
Discord Player is event-based. Let’s add listeners for important events:
// Emitted when a track starts playing
player . events . on ( 'playerStart' , ( queue , track ) => {
queue . metadata . channel . send ( `Now playing ** ${ track . cleanTitle } **!` );
});
// Emitted when an error occurs
player . events . on ( 'error' , ( queue , error ) => {
console . error ( `Error in ${ queue . guild . name } : ${ error . message } ` );
});
// Emitted when a track is added to the queue
player . events . on ( 'audioTrackAdd' , ( queue , track ) => {
queue . metadata . channel . send ( `Added ** ${ track . cleanTitle } ** to the queue!` );
});
// Emitted when the queue is empty
player . events . on ( 'emptyQueue' , ( queue ) => {
queue . metadata . channel . send ( 'Queue finished!' );
});
The queue.metadata object is where you store custom data like the text channel reference. You’ll set this when creating the queue.
Step 4: Create the Play Command
Now let’s create a simple play command using Discord.js interaction commands:
const { SlashCommandBuilder } = require ( 'discord.js' );
const { useMainPlayer } = require ( 'discord-player' );
module . exports = {
data: new SlashCommandBuilder ()
. setName ( 'play' )
. setDescription ( 'Play a song' )
. addStringOption ( option =>
option
. setName ( 'query' )
. setDescription ( 'The song you want to play' )
. setRequired ( true )
),
async execute ( interaction ) {
const player = useMainPlayer ();
const channel = interaction . member . voice . channel ;
if ( ! channel ) {
return interaction . reply ( 'You need to be in a voice channel!' );
}
const query = interaction . options . getString ( 'query' , true );
// Defer the reply as searching and loading can take time
await interaction . deferReply ();
try {
const { track } = await player . play ( channel , query , {
nodeOptions: {
metadata: {
channel: interaction . channel ,
client: interaction . guild . members . me ,
requestedBy: interaction . user ,
},
},
});
return interaction . followUp ( `** ${ track . cleanTitle } ** enqueued!` );
} catch ( error ) {
return interaction . followUp ( `Something went wrong: ${ error . message } ` );
}
},
};
The useMainPlayer() hook allows you to access the player instance from anywhere in your code without passing it around.
Step 5: Add Queue Control Commands
Let’s add more commands to control the queue:
skip.js
pause.js
stop.js
queue.js
const { SlashCommandBuilder } = require ( 'discord.js' );
const { useQueue } = require ( 'discord-player' );
module . exports = {
data: new SlashCommandBuilder ()
. setName ( 'skip' )
. setDescription ( 'Skip the current song' ),
async execute ( interaction ) {
const queue = useQueue ( interaction . guild . id );
if ( ! queue || ! queue . isPlaying ()) {
return interaction . reply ( 'No music is currently playing!' );
}
queue . node . skip ();
return interaction . reply ( 'Skipped the current song!' );
},
};
Step 6: Register Commands and Start the Bot
Finally, let’s register the commands and start the bot:
const fs = require ( 'fs' );
const path = require ( 'path' );
// Load commands
client . commands = new Map ();
const commandsPath = path . join ( __dirname , 'commands' );
const commandFiles = fs
. readdirSync ( commandsPath )
. filter ( file => file . endsWith ( '.js' ));
for ( const file of commandFiles ) {
const command = require ( path . join ( commandsPath , file ));
client . commands . set ( command . data . name , command );
}
// Handle interactions
client . on ( 'interactionCreate' , async interaction => {
if ( ! interaction . isChatInputCommand ()) return ;
const command = client . commands . get ( interaction . commandName );
if ( ! command ) return ;
try {
await player . context . provide ({ guild: interaction . guild }, () =>
command . execute ( interaction )
);
} catch ( error ) {
console . error ( error );
const reply = {
content: 'There was an error executing that command!' ,
ephemeral: true ,
};
if ( interaction . replied || interaction . deferred ) {
await interaction . followUp ( reply );
} else {
await interaction . reply ( reply );
}
}
});
// Ready event
client . once ( 'ready' , () => {
console . log ( `Logged in as ${ client . user . tag } ` );
});
// Login
client . login ( 'YOUR_BOT_TOKEN' );
Don’t forget to wrap command execution in player.context.provide(). This allows the useMainPlayer() and useQueue() hooks to work properly.
Project Structure
Your final project structure should look like this:
my-music-bot/
├── commands/
│ ├── play.js
│ ├── skip.js
│ ├── pause.js
│ ├── stop.js
│ └── queue.js
├── index.js
├── package.json
└── .env
Running Your Bot
Deploy Commands
Register your slash commands with Discord: const { REST , Routes } = require ( 'discord.js' );
const fs = require ( 'fs' );
const path = require ( 'path' );
const commands = [];
const commandsPath = path . join ( __dirname , 'commands' );
const commandFiles = fs
. readdirSync ( commandsPath )
. filter ( file => file . endsWith ( '.js' ));
for ( const file of commandFiles ) {
const command = require ( path . join ( commandsPath , file ));
commands . push ( command . data . toJSON ());
}
const rest = new REST (). setToken ( 'YOUR_BOT_TOKEN' );
( async () => {
try {
console . log ( 'Started refreshing application (/) commands.' );
await rest . put (
Routes . applicationCommands ( 'YOUR_CLIENT_ID' ),
{ body: commands }
);
console . log ( 'Successfully reloaded application (/) commands.' );
} catch ( error ) {
console . error ( error );
}
})();
Run: node deploy-commands.js
Start Your Bot
You should see: Extractors loaded successfully
Logged in as YourBot#1234
Test the Bot
Join a voice channel in your Discord server
Use /play never gonna give you up to play a song
Try other commands like /skip, /pause, /queue
What’s Next?
Congratulations! You’ve built a working music bot. Here are some next steps:
Audio Filters Learn how to apply audio filters like bass boost and nightcore
Queue Management Explore advanced queue features like shuffle and repeat modes
Custom Extractors Create custom extractors for additional audio sources
Events Discover all available events for better control
Troubleshooting
Bot doesn’t join the voice channel
Make sure you have the GuildVoiceStates intent enabled
Check that the bot has permission to connect to the voice channel
Verify that you’re in a voice channel when using the play command
No audio is playing
Ensure FFmpeg is properly installed and accessible
Check that you’ve installed an opus library (mediaplex)
Verify extractors are loaded: check console for “Extractors loaded successfully”
Commands not working
Make sure you’ve registered the slash commands using deploy-commands.js
Wrap command execution in player.context.provide() as shown above
Check the console for error messages
”Cannot find module” errors
Run npm install to ensure all dependencies are installed
Check that file paths in require() statements are correct
Verify that the commands folder exists and contains the command files
Use player.scanDeps() to generate a dependency report that can help troubleshoot installation issues.