pixeltui

module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: MIT

README

pixeltui

A fast, minimal, cross-platform terminal music player — search, stream, and organize music from the command line, with a hybrid recommendation engine and first-class support for self-hosted libraries.

pixeltui demo

  • Fast & tiny — a single ~10 MB Go binary, minimal dependencies, no Electron, no daemon.
  • Works anywhere — macOS, Linux, and Windows (amd64 / arm64).
  • Few third parties — streams from YouTube Music out of the box; optionally point it at your own Subsonic/Navidrome server or a folder of local files.
  • Yours to keep — likes, playlists, and history are stored as open, portable files (M3U8 / XSPF / ListenBrainz JSON) you can take anywhere.
  • Synced lyrics — karaoke-style, auto-scrolling lyrics via LRCLIB (free, no account); works for YouTube, Subsonic, and local tracks alike.
  • Artist & album pages — drill into any artist (top songs · albums · singles, with Last.fm listener stats) or album (ordered tracklist, year), like the big apps.
  • Scrobbling — submit plays to Last.fm and/or ListenBrainz, with an offline spool so listens made without a connection aren't lost.

Contents


Requirements

Tool Required? Purpose
yt-dlp required Resolves YouTube Music audio streams.
mpv recommended Pause / seek / volume + OS "Now Playing". Without it, audio still plays via ffplay/afplay but transport controls are off.
ffmpeg optional ffprobe reads tags for local files; ffplay is a playback fallback.

pixeltui can install and repair these for you — see doctor.

Streaming a Subsonic/Navidrome server or playing local files does not require yt-dlp (those play directly).


Install

macOS / Linux — one line
curl -fsSL https://raw.githubusercontent.com/dotjarden/pixeltui/main/install.sh | sh

Auto-detects your CPU, downloads the right prebuilt binary, puts pixeltui on your PATH, and installs the playback dependencies (yt-dlp, mpv). No Go needed.

Windows — one line (PowerShell)
irm https://raw.githubusercontent.com/dotjarden/pixeltui/main/install.ps1 | iex

Skip the automatic dependency check/install with --nodoctor (curl … | sh -s -- --nodoctor) or PIXELTUI_NO_DOCTOR=1. You can always run pixeltui doctor --fix later.

Manual download

Grab the binary for your platform from the latest release:

Platform Asset
macOS, Apple Silicon (M1–M4) pixeltui-darwin-arm64
macOS, Intel pixeltui-darwin-amd64
Linux x86-64 pixeltui-linux-amd64
Linux ARM64 pixeltui-linux-arm64
Windows pixeltui-windows-amd64.exe

These are command-line programs — run them from a terminal, not by double-clicking (double-clicking a binary just opens it in a text editor). On macOS:

chmod +x pixeltui-darwin-arm64
xattr -d com.apple.quarantine pixeltui-darwin-arm64    # clear Gatekeeper
sudo mv pixeltui-darwin-arm64 /usr/local/bin/pixeltui
pixeltui doctor --fix
With Go installed
go install github.com/dotjarden/pixeltui/tui@latest   # binary installs as `tui`; rename to `pixeltui` if you like
pixeltui doctor --fix      # install yt-dlp + mpv
Updating
pixeltui update

Downloads the latest release build for your platform, verifies its checksum, and replaces the running binary in place. (You can also just re-run the install one-liner, or go install …@latest.)

Uninstall
pixeltui uninstall            # remove the binary, data dir, and bundled yt-dlp/mpv
pixeltui uninstall --keep-data   # keep your library + config; remove everything else
pixeltui uninstall -y         # skip the confirmation prompt

A full clean by default: removes the pixeltui binary, the ~/.pixeltui data directory (cache, graph, library, config) and the self-contained tools it installed there. On Windows it also strips the PATH entry the installer added. mpv installed via your system package manager is left in place (remove it yourself, e.g. sudo apt-get remove mpv).


Quick start

pixeltui                       # open the interactive player (search-first)
pixeltui "Get Lucky" "Daft Punk"   # start seeded from a track
pixeltui setup                 # configure Last.fm key, Subsonic, local & download folders
pixeltui doctor                # check everything is wired up

Recommendations use a free Last.fm API key. Search and playback work without one.


Controls

One rule keeps the keymap predictable for track actions:

lowercase acts on the highlighted track · SHIFT acts on what's playing. (e.g. f likes the selected track, F likes the one currently playing.)

Playback (always the now-playing track)

Key Action
play selected track
space pause / resume
← → (or h l) seek (step configurable in settings, default 10s)
n next track · ; jump to the playing track
+ / volume up / down

Track — lowercase = highlighted, Shift = now-playing

Key Action
f / F like / unlike (♥)
a add to queue (A = play next)
p / P add to playlist
d / D download
x / X mute artist for this session (X also skips)
. actions menu — everything above, plus go to artist / album & start station
⇧↵ start an endless station from the selection

Artist & album pages

Key Action
/ then !a <artist> artist page: top songs · albums · singles (+ Last.fm listener stats)
/ then !al <album> album search → full album page (ordered tracklist, year, durations)
on an album row open the album
esc back through pages (album → artist → results)
e queue everything on the page

Queue (Up Next pane — Tab switches focus)

Key Action
↑ ↓ navigate · j k reorder
del remove · s shuffle · r repeat · c clear
u undo the last clear / remove / shuffle

View & modes

Key Action
/ search the current source
b browse: Liked · playlists · charts · stats · Local · Subsonic (o on a playlist starts a blended station · u restores a deleted playlist)
y lyrics — [ ] nudge the sync, 0 resets
z autoplay · t sleep timer · , settings
Tab switch pane · ? all keys · q quit · esc back

Press ? in the app for the full list at any time.


Scrobbling

pixeltui can submit your plays to Last.fm and/or ListenBrainz:

  1. pixeltui setupScrobbling — paste your Last.fm API key + shared secret (both on the same page) and/or a ListenBrainz token, and enable it.
  2. For Last.fm, a one-time browser authorization runs at the end of setup (redo anytime with pixeltui scrobble-auth).

Plays submit at the standard 50% / 4-minute mark, with a now-playing update at track start. Plays made offline are spooled and retried on the next launch. Toggle scrobbling live in the in-app settings (,).


Commands

pixeltui                          open the player (search-first)
pixeltui [track] [artist]         start seeded from a track
pixeltui setup                    interactive config wizard
pixeltui scrobble-auth            authorize Last.fm scrobbling (one-time)
pixeltui update                   self-update to the latest release
pixeltui doctor [--fix]           check setup; --fix auto-installs/repairs deps
pixeltui reset [target]           wipe data: cache | graph | library | config | all
pixeltui uninstall [--keep-data]  remove pixeltui, data, and bundled tools
pixeltui export <playlist> [file] write a playlist as XSPF (portable)
pixeltui build-graph              build the offline recommendation graph (once)
pixeltui cache warm --artist X    pre-fetch an artist for offline use
pixeltui cache stats | clear      show / wipe the cache
pixeltui help                     full usage and flags

Common flags for the recommend/seed mode: -explore 0..10, -deep-cuts, -no-artist "A,B", -n N, -offline, -no-tui, -key <lastfm>, -dev.

doctor --fix self-resolves the keystone dependencies: it installs a self-contained yt-dlp binary into ~/.pixeltui/bin (no Python needed) and mpv — a bundle on macOS, the standalone build on Windows, your package manager on Linux.


Configuration

Run pixeltui setup, or edit ~/.pixeltui/config.json:

{
  "lastfm_key": "",
  "scrobble": {
    "enabled": false,
    "lastfm_secret": "",
    "lastfm_session": "",
    "lastfm_user": "",
    "listenbrainz_token": ""
  },
  "subsonic": { "url": "", "user": "", "pass": "" },
  "local_dirs": [],
  "download_dir": "",
  "explore": 5,
  "autoplay": true,
  "seek_step": 10,
  "charts": { "global": true, "country": "" }
}

Current charts (live YouTube Music top tracks — no API key needed) show in Browse and on the For You landing: charts.global (worldwide Top, on by default) and charts.country — a country name or 2-letter code, e.g. "United States" or "GB".

Every value can also be set by environment variable (env wins over the file):

Variable Meaning
LASTFM_API_KEY Last.fm API key for recommendations
LASTFM_API_SECRET Last.fm shared secret (scrobbling)
LISTENBRAINZ_TOKEN ListenBrainz user token (scrobbling)
PIXELTUI_SUBSONIC_URL / _USER / _PASS Subsonic/Navidrome server
PIXELTUI_LOCAL_DIRS local music folders (PATH-style list)
PIXELTUI_DOWNLOAD_DIR where downloads are saved
PIXELTUI_YTDLP / PIXELTUI_MPV override the yt-dlp / mpv binary path

The config file is written 0600 since it can hold a password.


Sources

pixeltui can pull music from three places; switch between them in the b browse menu.

  • YouTube Music (default) — search, radio, and lyrics with no account.
  • Subsonic / Navidrome — point it at your server in setup. Starred songs and playlists are browsable and stream directly (no yt-dlp).
  • Local files — add folders in setup; tags are read via ffprobe with a filename fallback. Plays directly.

/ searches whichever source you're currently browsing.


Downloads

Set a download folder (in setup), then press d on any YouTube track. Files are saved with embedded tags and cover art in a standard layout:

<download_dir>/Artist/Album/Title.ext

That's exactly what Subsonic/Navidrome expect to scan — drop the folder into your server's music library and it just works.


Library & interop

Everything pixeltui stores about your listening is kept in open formats under ~/.pixeltui/library/, so nothing is locked in:

  • Likes & playlists → M3U8 (export any playlist to XSPF with pixeltui export)
  • Listening history → ListenBrainz-style JSON
  • Up Next + session → restored on the next launch

Data directory

Everything lives under ~/.pixeltui/:

config.json      configuration
cache.db         stream-URL + API cache (bbolt)
graph.bin        offline recommendation graph
library/         likes, playlists, history
bin/             self-contained tools pixeltui installed (yt-dlp)
ytdlp-venv/      fast pip yt-dlp (optional)
mpv.app/         self-contained mpv (macOS, optional)

Reset any of it with pixeltui reset <target>. Installed tools (mpv, yt-dlp) are kept even on reset all.


Companion server (experimental)

pixeltui serve exposes your library and sources over HTTP so a phone (or any client) can browse, search, and stream from anywhere:

pixeltui serve                 # prints a pairing QR + code
pixeltui serve --url https://pixeltui.example.ts.net   # behind a tunnel
  • Pairing: scan the QR (or enter the URL + code) once; the device gets a saved token. Tokens live in ~/.pixeltui/devices.json (revocable).
  • Transport: REST for actions, Server-Sent Events for live state.
  • Streaming: Subsonic and local play directly (range-aware); YouTube is transcoded to AAC on the fly (needs yt-dlp + ffmpeg, which doctor --fix installs).
  • From anywhere: bring your own tunnel — Tailscale is the easy, private option; pass its address with --url.

A native companion app (Flutter) lives in clients/flutter/ (see its own README). It's an optional client — the server is the stable, documented contract it builds on, and the terminal player works without it.

Repository layout

tui/              the pixeltui Go app (terminal player + `serve`) — go.mod at root
clients/flutter/  optional Flutter companion app (removable; tui works standalone)

Build from source

Requires Go 1.25+.

make build        # → ./pixeltui  (builds ./tui)
make install      # → $PREFIX/pixeltui (default /usr/local/bin)
make release      # cross-compile for all platforms → dist/
make help         # all targets

The build version is embedded via -ldflags -X main.version (pixeltui version); plain make build derives it from git describe.

Releasing (maintainers)
scripts/release.sh v0.2.0     # tags + pushes; CI does the rest

Pushing a v* tag triggers .github/workflows/release.yml, which cross-builds every platform, writes SHA256SUMS, and publishes the GitHub release that pixeltui update pulls from. Tags with a suffix (e.g. v0.2.0-rc1) publish as pre-releases.


Troubleshooting

Run pixeltui doctor — it reports the status of every dependency, your API key, configured sources, the graph, and the cache, and tells you exactly how to fix anything that's missing. Add --fix to auto-resolve what it can.


License

pixeltui is released under the MIT License.

The Go libraries compiled into the binary are all permissive (MIT / BSD-3-Clause). The external tools pixeltui drives — yt-dlp, mpv, and ffmpeg — are run as separate, user-installed programs; they are not bundled with or linked into pixeltui, so their licenses (Unlicense, GPL/LGPL) apply only to those tools. Full attribution and per-license details are in THIRD_PARTY_NOTICES.md.

Directories

Path Synopsis
tui
config
Package config manages pixeltui's JSON config file at <dataDir>/config.json.
Package config manages pixeltui's JSON config file at <dataDir>/config.json.
download
Package download saves tracks to a local music library using yt-dlp, with embedded tags + cover art and an Artist/Album/Title folder layout — i.e.
Package download saves tracks to a local music library using yt-dlp, with embedded tags + cover art and an Artist/Album/Title folder layout — i.e.
library
Package library manages a portable, interoperable on-disk music library under ~/.pixeltui/library/.
Package library manages a portable, interoperable on-disk music library under ~/.pixeltui/library/.
local
Package local indexes local audio files in given folders and exposes them as engine.Candidate tracks.
Package local indexes local audio files in given folders and exposes them as engine.Candidate tracks.
lyrics
Package lyrics fetches song lyrics from LRCLIB (https://lrclib.net) — a free, open, no-auth lyrics database that provides synced (timestamped) and plain lyrics.
Package lyrics fetches song lyrics from LRCLIB (https://lrclib.net) — a free, open, no-auth lyrics database that provides synced (timestamped) and plain lyrics.
scrobble
Package scrobble submits plays to Last.fm and ListenBrainz.
Package scrobble submits plays to Last.fm and ListenBrainz.
server
Package server exposes pixeltui's library and sources over HTTP so a phone (or any client) can browse, search, and stream "from anywhere" via a BYO tunnel.
Package server exposes pixeltui's library and sources over HTTP so a phone (or any client) can browse, search, and stream "from anywhere" via a BYO tunnel.
store
Package store handles all local persistence: the bbolt response cache and the static pre-built artist graph.
Package store handles all local persistence: the bbolt response cache and the static pre-built artist graph.
subsonic
Package subsonic is a minimal OpenSubsonic/Subsonic REST API client.
Package subsonic is a minimal OpenSubsonic/Subsonic REST API client.
ytm
Package ytm wraps the pure-Go YouTube Music client (raitonoberu/ytmusic), adapting its results to engine.Candidate so search, album art, duration, and "radio" recommendations all flow through the same type used by the TUI.
Package ytm wraps the pure-Go YouTube Music client (raitonoberu/ytmusic), adapting its results to engine.Candidate so search, album art, duration, and "radio" recommendations all flow through the same type used by the TUI.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL