spot: A Spotify Client Built on Consult, Embark, and Marginalia
Table of Contents
- 1. About emacs spotify completion
- 2. Why Another Spotify Client spotify motivation
- 3. Architecture emacs design
- 4. Search and Navigation spotify consult
- 5. Demo: Search and Playback spotify demonstration
- 6. Marginalia Annotations marginalia
- 7. Embark Actions actions
- 8. Mode Line emacs modeline
- 9. Player Controls spotify playback
- 10. Getting Started emacs installation
1. About emacs spotify completion
Figure 1: JPEG produced with DALL-E 4o
There are a few Spotify clients for Emacs. Most of them predate the modern completion ecosystem, so they build their own UI from scratch: custom buffers, custom keymaps, custom rendering. spot takes a different approach. It's built entirely on consult, embark, and marginalia, which means searching, acting on results, and reading metadata all use the same interfaces you already know.
2. Why Another Spotify Client spotify motivation
smudge (formerly spotify.el) is the most established Emacs Spotify client. It works, but it renders results in a custom tabulated-list buffer. You search, wait for a buffer to pop up, navigate it with its own keybindings, and act on items through its own action system. This is a parallel universe of interaction that doesn't benefit from any of the completion infrastructure you've invested in.
spot doesn't have its own UI idioms to learn. The UI is consult + embark + marginalia.
If you use vertico or selectrum for completion, embark for contextual actions, and marginalia for annotations, you already have a sophisticated interaction model. spot plugs into that model directly. Search results appear in your minibuffer completion framework. Metadata shows up as marginalia annotations. Actions are embark actions. There's nothing new to learn, just Spotify data flowing through tools you already use.
3. Architecture emacs design
spot is modular by design. Each concern lives in its own file:
- spot-auth.el: OAuth2 authorization code flow with token refresh
- spot-search.el: Mutex-guarded, cached search with argument parsing
- spot-consult.el: Seven async consult sources (albums, artists, tracks, playlists, shows, episodes, audiobooks)
- spot-marginalia.el: Annotation functions per content type
- spot-embark.el: Action keymaps per content type
- spot-mode-line.el: Currently-playing display with smart update timing
The search layer deserves a note. spot uses a mutex to serialize concurrent search requests and caches results by query string. This matters because consult's async sources fire searches as you type, and Spotify's API has rate limits. Without the mutex, rapid keystrokes could produce interleaved results or hit rate limits. The cache prevents redundant API calls when you backspace and retype.
3.1. OAuth2 Flow
Spotify requires OAuth2 for most API operations. spot handles the authorization code grant flow: it opens the Spotify authorization URL in your browser, you log in and approve, then paste the authorization code back into Emacs. The access token is stored in memory for the session, and spot refreshes it automatically when it expires. You set spot-client-id and spot-client-secret from a Spotify Developer application.
5. Demo: Search and Playback spotify demonstration
Here's a search session showing multi-source results, marginalia annotations, and playback control.
6. Marginalia Annotations marginalia
Each content type has its own marginalia annotator that pulls relevant metadata from the Spotify API response. Here's what you see in the completion margin for each type:
| Type | Annotations |
|---|---|
| Albums | Artist, release date, track count |
| Artists | Popularity score, follower count |
| Tracks | Track number, artist, duration, album, release date |
| Playlists | Track count |
| Shows | Publisher, media type, episode count, description |
| Episodes | Release date, duration, description |
| Audiobooks | Publisher, narrator, author, description |
This is pure marginalia integration. The annotators register themselves when spot-mode is enabled and unregister when it's disabled. If you don't use marginalia, spot still works, you just don't see the extra metadata.
7. Embark Actions actions
spot defines embark keymaps for every content type. When you press your embark-act key on a completion candidate, you get actions appropriate to that type:
| Key | Action | Available On |
|---|---|---|
P |
Play | All types |
s |
Show raw JSON data | All types |
t |
List tracks | Albums, artists, playlists |
+ |
Add to playlist | Tracks only |
Listing an album's tracks, for example, opens a new consult search narrowed to that album's tracks. From there you can play individual tracks, inspect them, or add them to a playlist, all through the same embark action system.
8. Mode Line emacs modeline
When spot-mode is enabled, the mode line shows the currently playing track, artist, and album. The display updates on a 30-second timer, with smart scheduling that calculates the time remaining in the current track to minimize unnecessary API calls. The currently-playing text is displayed in Spotify green (#1db954) by default.
9. Player Controls spotify playback
spot provides standard player commands, all operating through Spotify Connect:
(spot-player-play) ; Resume playback (spot-player-pause) ; Pause playback (spot-player-next) ; Skip to next track (spot-player-previous) ; Skip to previous track
There's also spot-add-current-track-to-playlist, which prompts you to select from your playlists (via consult, naturally) and adds whatever is currently playing.
10. Getting Started emacs installation
spot is on GitHub. It requires Emacs 29.1+ and depends on consult, embark, marginalia, ht, and dash.
(use-package spot
:ensure (:host github :repo "chiply/spot")
:config
(setq spot-client-id "your-client-id"
spot-client-secret "your-client-secret")
(spot-mode 1))
You'll need to create a Spotify Developer application at https://developer.spotify.com/dashboard and set the redirect URI to http://localhost:8080/callback. On first use, run M-x spot-authorize to complete the OAuth2 flow. After that, M-x spot and start typing.