Benjamin Shawki
Potters.Tools

Showing Now Playing Music in Waybar with playerctl

WaybarSwayLinuxShell ScriptMusicCustomization

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:

bash
#!/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:

json
"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:

css
#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.

Showing Now Playing Music in Waybar with playerctl | Potters.Tools Blog | Potters Tools