██████ ██ ██ █████ ███ ███ ██████ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ███████ ██ ████ ██ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ ███████ ██ ██ ██ ██ ██ ██
Playlists, EQ, visualizers, lyrics, remote control, and a Lua plugin system. Streams from Spotify, YouTube Music, NetEase, Plex, Jellyfin, Emby, Navidrome, and 30,000+ radio stations.
$ cliamp https://radio.cliamp.stream/lofi/stream.pls
$ curl -fsSL …/install.sh | sh
COPIED
$ brew install bjarneo/cliamp/cliamp
COPIED
$ yay -S cliamp
COPIED
Stream from everywhere. Every provider runs through the same playlist, EQ, visualizer, and lyrics pipeline — your config follows you across services. Run cliamp setup for an interactive wizard that walks you through Navidrome, Plex, Jellyfin, Emby, Spotify, NetEase, and YouTube Music.
client_id for a private quota, or use the built-in shared one. Search & add tracks with F.[soundcloud] enabled = true. Search with Ctrl+F, browse curated genre playlists, or add user for your profile. cookies_from unlocks Go+ tracks via your browser session.cliamp setup after signing in at music.163.com, then choose your browser. Browse liked songs, account playlists, saved playlists, and charts with browser-cookie playback.Sign in at music.163.com, then run cliamp setup. Choose NetEase Cloud Music, pick the browser you used, and the wizard validates the session before writing config.
[netease]
enabled = true
cookies_from = "chrome"
user_id = "your-account-user-id"
After setup, press M to open NetEase and Ctrl+F to search. Playback uses yt-dlp with the same browser cookie source.
Parametric EQ with presets: Rock, Jazz, Pop, Classical, and more.
17 built-in themes and multiple visualizer modes. Hot-swap with t / v.
TOML playlists, M3U/M3U8/PLS support, playlist manager with album grouping.
Auto-recorded listening history. Browse it as a virtual playlist or run cliamp history from the shell.
Play from URLs, internet radio, and remote M3U playlists.
Auto-scrolling lyrics for local & Navidrome tracks. Press y.
Seamless transitions with preloaded next-track buffering.
Desktop integration. Hardware media keys and playerctl.
Configurable sample rate (22kHz–192kHz), buffer size, resample.
ICY/Shoutcast metadata — current song on internet radio streams.
ID3v2, Vorbis comments, MP4 atoms — artist, album, genre, year.
Override volume, shuffle, repeat, theme, EQ, sample rate per-session.
File logs at ~/.config/cliamp/cliamp.log. Set log_level in config or --log-level on the CLI: debug, info, warn, error.
Run --upgrade to update to the latest release in-terminal.
Reference any string in config.toml from the environment with ${VAR} or $VAR. Keep passwords and tokens out of the file.
Control a running instance from another terminal via Unix-socket IPC. Run with --daemon for headless playback driven entirely by scripts or Waybar.
Lua 5.1 sandboxed plugin system. Hook events, add visualizers, push data.
Press Ctrl+S to save the current track to ~/Music.
17 built-in themes. Press t to switch, or use --theme "name" from the CLI.
Drop a .toml file in ~/.config/cliamp/themes/ to add your own.
CLI flags override any config option for a single session.
Remote commands control a running instance over Unix-socket IPC — open another terminal and talk to cliamp directly.
Lua 5.1 plugin system. Hook into playback events, add custom visualizers, or push data to external services.
Each plugin runs in an isolated sandbox — a crash in one cannot affect others or the player.
Drop a .lua file in ~/.config/cliamp/plugins/ and restart.
-- ~/.config/cliamp/plugins/now-playing.lua local p = plugin.register({ name = "now-playing", type = "hook", }) p:on("track.change", function(track) cliamp.fs.write("/tmp/cliamp-now-playing", track.artist .. " - " .. track.title) end) p:on("app.quit", function() cliamp.fs.remove("/tmp/cliamp-now-playing") end)
-- ~/.config/cliamp/plugins/webhook.lua local p = plugin.register({ name = "webhook", type = "hook", }) local url = p:config("url") p:on("track.change", function(track) if not url then return end cliamp.http.post(url, { json = { title = track.title, artist = track.artist } }) end)
-- ~/.config/cliamp/plugins/simple-bars.lua local p = plugin.register({ name = "simple-bars", type = "visualizer", }) -- Called ~20 FPS. bands: 10 values (0.0-1.0) function p:render(bands, frame) local lines = {} for row = 5, 1, -1 do local line = "" for i = 1, 10 do if bands[i] > (row - 1) / 5 then line = line .. "██████ " else line = line .. " " end end table.insert(lines, line) end return table.concat(lines, "\n") end
Read-only state: state(), position(), volume(), shuffle(), eq_bands()…
Current metadata: title(), artist(), album(), genre(), duration_secs()
HTTP client: get(), post(). JSON, form body, headers, 5s timeout.
Sandboxed I/O: write(), read(), append(), remove(), exists(), mkdir(), listdir()
Spawn from allowlist (yt-dlp, ffmpeg by default): run(bin, args, opts), streams stdout/stderr, cancelable. Requires permissions = {"exec"}.
encode() and decode() for JSON serialization.
md5(), sha256(), hmac_sha256() for hashing and signing.
Desktop notifications via notify-send. Works with mako, dunst, etc.
message(text, secs) → transient message in the player's status bar.
Schedule callbacks: after(), every(), cancel()
info(), warn(), error(), debug() → plugins.log
Read per-plugin values from [plugins.name] in config.toml via p:config(key)
p:bind(key, desc, fn) — register a TUI keybinding. With a description it appears in the Ctrl+K overlay. Rejects keys reserved by cliamp. Requires permissions = {"keymap"}.
p:command(name, fn) — register a shell-invokable command. Run with cliamp plugins call <plugin> <cmd> [args].