Showing Now Playing Music in Waybar with playerctl
Waybar supports custom modules that execute a script and consume its JSON output. With a short shell script and playerctl, you can display the currently playing track from Tidal, Spotify, or any MPRIS-compatible media player directly in your status bar.
The Script
The entire script fits in 35 lines:
#!/bin/bash
STATUS=$(playerctl status 2>/dev/null)
if [ $? -ne 0 ]; then
echo '{"text": "", "class": "no-player", "tooltip": "No media players running"}'
exit 0
fi
ARTIST=$(playerctl metadata artist 2>/dev/null)
TITLE=$(playerctl metadata title 2>/dev/null)
if [ "$STATUS" == "Playing" ]; then
ICON="♪"
CLASS="playing"
else
ICON="⏸"
CLASS="paused"
fi
if [ -n "$ARTIST" ] && [ -n "$TITLE" ]; then
DISPLAY="$ARTIST - $TITLE"
if [ ${#DISPLAY} -gt 40 ]; then
DISPLAY="${DISPLAY:0:37}..."
fi
else
DISPLAY="$TITLE"
fi
echo "{\"text\": \"$ICON $DISPLAY\", \"class\": \"$CLASS\", \"tooltip\": \"$ARTIST - $TITLE\"}"playerctl queries the currently active MPRIS media player. The script checks if anything is running, grabs the artist and title metadata, truncates to 40 characters so it does not eat the entire status bar, and outputs JSON that Waybar understands. The class field lets us style playing and paused states differently.
The Waybar Configuration
In config.jsonc, the custom module runs the script every second and wires up click and scroll actions:
"custom/media": {
"format": "{}",
"return-type": "json",
"exec": "$HOME/bin/tidal-now-playing.sh",
"interval": 1,
"on-click": "playerctl play-pause",
"on-scroll-up": "playerctl next",
"on-scroll-down": "playerctl previous"
}Click to play/pause. Scroll up for next track, scroll down for previous. The module goes in modules-right alongside the usual suspects like clock, battery, and volume.
Styling
The CSS uses different Tokyo Night colors for each state:
#custom-media {
min-width: 100px;
color: #bb9af7;
}
#custom-media.playing {
color: #9ece6a;
}
#custom-media.paused {
color: #e0af68;
}Purple (#bb9af7) as the default, green (#9ece6a) when playing, yellow (#e0af68) when paused. The class field in the JSON output maps directly to these CSS selectors -- Waybar adds the class to the module's element automatically.
Making It Your Own
The script is completely player-agnostic. playerctl talks to whatever MPRIS-compatible player is running -- Tidal, Spotify, Firefox playing YouTube, VLC, mpv, anything. If you run multiple players simultaneously, playerctl picks the most recently active one, or you can target a specific player with playerctl -p tidal-hifi.
The 1-second polling interval is a trade-off. It is fast enough that track changes appear nearly instantly, but it means the script runs once per second even when nothing is playing. If that bothers you, Waybar also supports "exec-on-event": true with "signal": 8 for manual refresh via pkill -RTMIN+8 waybar, but for a script this lightweight the polling approach is simpler.
