Live now playing
NowPlayingFeed connects your bot to your website. The site shows your bot (avatar, name, online status) and the song it’s playing right now, with artwork and a live progress bar. It updates the moment a song starts, pauses or ends.
Only the song is shared: no server names, server ids or usernames.
How it works
Section titled “How it works”Websites can’t ask Discord what a bot is playing, so your bot shares it:
Lavalink → your bot (Raya + NowPlayingFeed) → your website GET /now-playing JSON snapshot GET /now-playing/stream live updates (Server-Sent Events)Connect your bot
Section titled “Connect your bot”-
Add the feed to your bot.
botis how your bot appears on the site. A function keeps the name and avatar up to date:bot.js const { NowPlayingFeed } = require('raya.js');const feed = new NowPlayingFeed({port: 8787,cors: 'https://neuzgg.github.io', // your website originbot: () => ({name: client.user?.username ?? 'Raya',avatar: client.user?.displayAvatarURL({ size: 256 }),url: 'https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID', // optional "Add to Discord" button}),});await raya.use(feed); -
Check it works. Play something and open
http://localhost:8787/now-playing. You should see your bot and the song. -
Make it reachable over HTTPS. Websites on HTTPS (like GitHub Pages) can’t load
http://feeds. The easiest way is a Cloudflare Tunnel, which also works if your bot runs at home:Terminal window cloudflared tunnel --url http://localhost:8787It prints a
https://….trycloudflare.comaddress. For a permanent address, use a named tunnel or a reverse proxy (Caddy, Nginx) with your own domain. -
Point your website at it. Read the feed with a few lines of JavaScript: see Show it on any website.
While the bot is offline or nothing is playing, the feed says so, so your site never has to show fake data.
Which song is shown
Section titled “Which song is shown”If your bot plays in several servers at once, the site keeps showing the same song while it plays, so the card doesn’t jump around. When that song pauses or ends, it switches to the most recently started song. To keep some servers out completely, pass a filter:
new NowPlayingFeed({ bot: () => ({ name: client.user.username, avatar: client.user.displayAvatarURL() }), nowPlaying: (guildId) => !PRIVATE_SERVERS.has(guildId), // these servers never supply the song});Optional: per-server cards
Section titled “Optional: per-server cards”To also show individual servers that opt in, add publish. It returns how a server appears, or null to keep it private:
const PUBLIC_SERVERS = new Set(['123456789012345678']);
new NowPlayingFeed({ bot: () => ({ name: client.user.username, avatar: client.user.displayAvatarURL() }), publish: (guildId) => { const guild = PUBLIC_SERVERS.has(guildId) && client.guilds.cache.get(guildId); return guild ? { name: guild.name, icon: guild.iconURL() } : null; },});Use your existing web server
Section titled “Use your existing web server”Leave out port and mount the feed on a server you already run:
const feed = new NowPlayingFeed({ bot });await raya.use(feed);
app.get(['/now-playing', '/now-playing/stream'], (req, res) => feed.handle(req, res));http.createServer((req, res) => { if (feed.handle(req, res)) return; // handled by the feed res.writeHead(404).end();}).listen(3000);Show it on any website
Section titled “Show it on any website”You don’t need this docs site. A few lines of plain JavaScript are enough:
<p id="now-playing">Connecting…</p><script> const text = document.getElementById('now-playing'); const feed = new EventSource('https://your-address/now-playing/stream');
feed.addEventListener('snapshot', (event) => { const { bot, nowPlaying } = JSON.parse(event.data); text.textContent = nowPlaying ? `${bot.name} is playing ${nowPlaying.track.title} by ${nowPlaying.track.author}` : `${bot.name} is not playing anything`; }); feed.addEventListener('error', () => (text.textContent = 'Bot offline'));</script>Always set feed data with textContent, not innerHTML, because song titles come from the internet.
Data format
Section titled “Data format”{ "bot": { "name": "Raya", "avatar": "https://cdn.discordapp.com/avatars/.../....png", "url": "https://discord.com/oauth2/authorize?client_id=...", "uptime": 11520000 }, "nowPlaying": { "track": { "title": "Blinding Lights", "author": "The Weeknd", "uri": "https://open.spotify.com/track/...", "artworkUrl": "https://i.scdn.co/image/...", "source": "spotify", "duration": 200040, "isStream": false }, "position": 64250, "paused": false, "speed": 1 }, "players": [], "totals": { "playing": 3 }, "time": 1789000000000}nowPlaying is null when nothing is playing. position is the position when the snapshot was sent. To animate a progress bar, add (now - receivedAt) × speed while not paused. A new snapshot arrives on every song change, pause, seek or stop, and when the position drifts by more than 2 seconds.
Options
Section titled “Options”| Option | Default | Description |
|---|---|---|
bot |
– | { name, avatar?, url? } or a function returning it. Enables the bot and its current song |
nowPlaying |
true with bot |
false, or (guildId) => boolean to choose which servers can supply the song |
publish |
– | Optional per-server cards: (guildId) => { name, icon? } | null |
port |
– | Start a standalone server on this port |
host |
all interfaces | Interface for the standalone server |
path |
'/now-playing' |
Snapshot URL; the stream is at path + '/stream' |
cors |
'*' |
Access-Control-Allow-Origin |
interval |
2000 |
ms between checks for pauses, seeks and drift |
maxClients |
500 |
Maximum simultaneous stream connections |
includeTotals |
true |
Include how many servers are playing (a count only) |
You need bot, publish, or both.
| Member | Description |
|---|---|
feed.url |
Standalone server URL once listening |
feed.connections |
Connected stream clients |
feed.snapshot() |
Current data |
feed.refresh() |
Push an update to every client now |
feed.handle(req, res) |
Serve the feed on your own server; returns false for other URLs |