A vim-modal Spotify client for the terminal.
Install · Features · Keybindings · Commands
Homebrew (macOS / Linux):
brew trust danfry1/tap # one-time: trust the third-party tap
brew install danfry1/tap/waxon
Recent Homebrew versions refuse to load formulae from third-party taps until they're trusted. If you see
Error: Refusing to load formula ... from untrusted tap, run thebrew trust danfry1/tapline above (once) and retry.Upgrading from ≤ 1.6.0? waxon moved from a formula to a cask. Once:
brew uninstall --formula waxon && brew install danfry1/tap/waxon.
Nix (flakes):
nix run github:danfry1/waxon # run without installing
nix profile install github:danfry1/waxon
Scoop (Windows):
scoop bucket add danfry1 https://github.com/danfry1/scoop-bucket
scoop install waxon
Go:
go install github.com/danfry1/waxon@latest
Linux packages / binaries: every release
ships .deb, .rpm and .apk packages plus tar.gz/zip archives (with
SHA-256 checksums) for Linux, macOS and Windows on amd64 and arm64.
waxon # first run walks you through connecting SpotifySetup asks one question — use your own Spotify app (recommended: no rate limits, two minutes, waxon shows the exact clicks) or the shared one (zero setup, but Spotify throttles it for everyone).
Re-run or switch any time:
waxon auth # guided
waxon auth --own # set up / switch to your own app
waxon auth --shared # use the shared app
waxon auth --client-id <ID>Requires a Spotify Premium account. True-colour terminals get the full look; 256-colour and NO_COLOR terminals are supported with graceful fallbacks.
waxon is a remote control for Spotify Connect: it plays through whatever Spotify client is running (desktop app, phone, speaker). If nothing is active when you press play, waxon picks the only available device for you — or asks which one to use when there are several.
Navigate everything without leaving the home row — j/k to move, gg/G to jump, h/l to switch panes.
Below 80 columns waxon shows one pane at a time — h/l/Tab switch between library and tracks — and the track table drops columns rather than overflowing. Now Playing art scales to fit. Handy for a tmux side pane.
Full-screen album art rendered with Unicode half-blocks, gradient backgrounds, and a vinyl spinning mode.
Press l in Now Playing for time-synced lyrics (via lrclib — no account, no API key, nothing to set up). The current line lights up and the rest gently fade as the song plays.
o on any track offers Add to Playlist… (type to filter your playlists) and, inside one of your own playlists, Remove from …. Create a new one with :playlist new <name>. Changes show up immediately in the sidebar and the open playlist.
Upgrading? Playlist editing needs two extra Spotify permissions; waxon will ask you to run
waxon authonce.
Find tracks, artists, and albums across Spotify.
Explore discographies, browse full albums, and navigate with a browser-like back stack.
Vim-style commands for volume, shuffle, repeat, device switching, and more.
| Key | Action |
|---|---|
j / k |
Move down / up |
gg |
Go to top |
G |
Go to bottom |
Ctrl+u / Ctrl+d |
Half page up / down |
| Key | Action |
|---|---|
h / l |
Focus left / right pane |
Tab |
Cycle pane |
1 / 2 |
Library / queue section |
| Key | Action |
|---|---|
gl |
Go to library |
gq |
Go to queue |
gc |
Jump to currently playing track (loads its playlist/album if you've navigated away) |
gr |
Recently played |
| Key | Action |
|---|---|
Space |
Play / pause |
Enter |
Play selected |
n / p |
Next / previous track |
[ / ] |
Seek -5s / +5s |
| Key | Action |
|---|---|
o |
Context actions menu |
a |
Add to queue |
/ |
Filter current view |
s |
Spotify search |
D |
Device switcher |
: |
Command mode |
| Key | Action |
|---|---|
N |
Now Playing view |
f / a / o |
Like / queue / actions for the playing track (in Now Playing) |
V |
Toggle vinyl mode (in Now Playing) |
l |
Toggle synced lyrics (in Now Playing) |
Backspace / b |
Go back |
? |
Toggle help overlay |
q |
Quit |
Esc |
Close / cancel |
Enter command mode by pressing :, then type a command.
| Command | Description |
|---|---|
:vol <0-100> |
Set volume |
:shuffle |
Toggle shuffle |
:repeat off|all|one |
Set repeat mode |
:device |
Open device switcher |
:search <query> |
Search Spotify |
:recent |
Recently played |
:theme <name> |
Switch colour theme (saved to config) |
:playlist new <name> |
Create a playlist |
:q |
Quit |
waxon reads ~/.config/waxon/config.json ($XDG_CONFIG_HOME respected; waxon config prints the path). Everything is optional:
{
"theme": "catppuccin-mocha",
"colors": { "accent": "#F5C2E7" },
"keys": { "next": "l,right", "prev": "h,left", "quit": "ctrl+q" }
}Themes — built in: spotify (default), catppuccin-mocha, catppuccin-latte, gruvbox, tokyonight, nord, dracula, rose-pine (waxon themes lists them; waxon doesn't paint the terminal background, so pick a light theme like catppuccin-latte for a light terminal). Switch live with :theme <name> — it's saved to your config. colors overrides individual palette entries on top of any theme: accent, bg, surface, text, text_sec, text_dim, border, error, mode_search, mode_filter, overlay.
Keys — keys maps an action to a comma-separated list of keys (Bubbletea names: j, down, ctrl+d, space, enter, esc, tab, backspace, f1…). Actions: up down bottom half_up half_down focus_left focus_right cycle_pane enter play_pause next prev seek_fwd seek_back add_queue like actions devices back filter search command help now_playing quit escape section1 section2. The g-prefix motions (gg gl gq gc gr) are fixed. The ? help overlay always shows your live bindings.
Cache — album art is cached under $XDG_CACHE_HOME/waxon/images (or your OS cache dir), capped at 64 MB and pruned on launch, so relaunching paints the library instantly. Delete the directory to clear it.
Colour fallback — colours degrade automatically on 256-colour terminals and are dropped under NO_COLOR; album art uses the 256-colour cube where true colour isn't available and is skipped entirely on monochrome terminals.
Every playback action is also a plain subcommand, so waxon slots into tmux, waybar/polybar, hotkey daemons and shell scripts without opening the TUI.
waxon status # ▶ Let It Happen — Tame Impala
waxon status --json # {"playing":true,"title":...,"progress":37,...}
waxon status --waybar # {"text":"…","alt":"playing","class":"playing",...}
waxon status --format '{icon} {title} [{position}/{duration}]'
waxon play | pause | toggle | next | prev
waxon play daft punk get lucky # search, then play the first match
waxon seek +10 | seek -10 | seek 1:30
waxon vol 40 | vol +5 | vol -5
waxon shuffle [on|off] | repeat off|all|one
waxon like # toggle Liked Songs for the playing track
waxon queue instant crush
waxon devices | device "living room"
waxon search radiohead [--json]
status prints nothing when idle (so bars stay blank) and exits non-zero on
errors; --json always emits an object. Placeholders for --format:
{title} {artist} {album} {position} {duration} {progress} {state} {icon} {device} {volume} {shuffle} {repeat} {liked} {uri} {id}. If no Spotify device
is active, commands activate the only available one automatically, or tell you
which ones to choose from.
tmux (~/.tmux.conf):
set -g status-right '#(waxon status --format "{icon} {title} — {artist}") | %H:%M'
set -g status-interval 5
waybar (~/.config/waybar/config) — --waybar emits the
text/alt/class/tooltip/percentage object waybar expects:
"custom/spotify": {
"exec": "waxon status --waybar",
"return-type": "json",
"format": "{icon} {}",
"format-icons": {"playing": "", "paused": "", "idle": ""},
"on-click": "waxon toggle",
"on-scroll-up": "waxon vol +5",
"on-scroll-down": "waxon vol -5",
"interval": 5
}Hotkeys (skhd on macOS / sxhkd on Linux):
cmd + alt - space : waxon toggle
cmd + alt - right : waxon next
cmd + alt - left : waxon prev
cmd + alt - l : waxon like
waxon works out of the box with no configuration — it ships with a shared client ID used by several open-source Spotify clients. Spotify rate-limits that shared app as a whole, so at busy times you may see Spotify rate limit toasts or sluggish controls. A personal client ID has its own quota and takes two minutes to set up:
-
Go to the Spotify Developer Dashboard and create an app
-
Set the redirect URI to
http://127.0.0.1:27228/callback -
Copy the Client ID and run setup with it:
SPOTIFY_CLIENT_ID=your_client_id waxon auth
The client ID is saved to ~/.config/waxon/config.json automatically, so you only need to set the environment variable once during setup.
Note: new developer apps are in Spotify's development mode, which only allows the current API endpoints. waxon uses those (with legacy fallbacks for older apps), so playback, search, queue, liking and playlist editing all work. The one thing Spotify blocks for development-mode apps with no replacement is an artist's top tracks — artist pages show the discography instead.
| Symptom | Cause / fix |
|---|---|
| Spotify rate limit / Spotify is rate limiting toasts | Spotify is throttling the app. waxon backs off automatically and honours Spotify's Retry-After. If it keeps happening, the shared client ID is busy — use your own. |
| No active Spotify device | Spotify must be open somewhere (desktop, phone, speaker). waxon picks the only available device automatically or asks with D. |
| Not available with this Spotify app | Spotify blocks that endpoint for development-mode (personal) apps — currently only artists' top tracks. |
Permission needed — run waxon auth |
Your saved token predates a feature that needs extra permissions (e.g. playlist editing). Re-run waxon auth once. |
| Session expired | Token revoked or refresh failed. Re-run waxon auth. |
| Nothing renders / garbled colours | Set a true-colour or 256-colour TERM, or NO_COLOR=1 for monochrome. Minimum size is 40×10. |
Debug log: WAXON_LOG=/tmp/waxon.log waxon (includes rate-limit Retry-After values).
| Variable | Description |
|---|---|
SPOTIFY_CLIENT_ID |
Override the saved Spotify Client ID |
WAXON_LOG |
Path to debug log file (e.g. /tmp/waxon.log) |
WAXON_NO_BROWSER |
Don't launch a browser during waxon auth; just print the URL (SSH) |
Built with Bubbletea, Bubbles, and Lip Gloss by Charmbracelet. Demo recordings made with VHS. Huge thanks to the Charm team for making terminal UIs a joy to build.
This project is licensed under the GNU General Public License v3.0.













