Muzical is a terminal music browser and launcher built with
Ink and React. It indexes audio files
under a configurable folder, reads tags with
music-metadata, and hands playback
off to whichever common CLI media player it finds first on your PATH.
Summary. Five views — a three-column library browser, a playlist queue, a "now playing" pane, a Soulseek search/download view, and a live config editor — all driven by vim-style keybindings.
1–5.hjkl) and arrow-key navigation./; queries are
independent per pane (artist, album, song, playlist).space; remove entries or
clear the whole list from the playlist view.musicDir.musicDir, extensions, and Soulseek
credentials without leaving the TUI; changes persist to disk immediately.music-metadata).+ / - adjusts master volume; mpv volume is
updated over the IPC socket while a track is playing.mpg123, mpv, ffplay,
or vlc if available.>= 16 (see engines in package.json).PATH (see
Playback backends).git clone https://github.com/f3rnox/muzical.git
cd muzical
pnpm install
pnpm build
Run locally without installing globally:
pnpm start
# or, during development:
pnpm dev
Link the CLI globally (optional):
pnpm link --global
muzical
When published to npm, install with:
pnpm add -g muzical
# or: npm install -g muzical
Muzical reads config.json from the OS config directory for the muzical
application name (via env-paths):
~/.config/muzical/config.json~/Library/Preferences/muzical/config.json%APPDATA%\muzical\config.jsonPass --config <path> to use a file at an arbitrary location. If the default
config directory does not exist it is created on startup. If config.json is
missing and --music-dir is not supplied, the program exits with a message
showing the expected path.
config.json shape{
"musicDir": "/absolute/path/to/your/music",
"songExtensions": [".mp3", ".flac"],
"soulseekUsername": "",
"soulseekPassword": ""
}
Fields:
musicDir (required) — Root directory scanned recursively for audio
files.songExtensions (optional) — Allowed suffixes; defaults to
[".mp3", ".flac"].soulseekUsername (optional) — Used by the Soulseek view to connect.
Empty by default.soulseekPassword (optional) — Stored in plaintext alongside the
username. Empty by default.The Soulseek fields can also be edited live from the in-app Config view
(press 5).
muzical
On launch, Muzical clears the screen, loads the library (this can take a moment
on large collections), renders the TUI, and clears again on exit. Use
--no-clear-screen to keep terminal scrollback.
1 – 5 to switch between Library, Playlist, Now Playing,
Soulseek, and Config views.h / l) to move focus between Artists,
Albums, and Songs (library view only).j / k), Page Up/Down, or g / G to move
through the focused list./ to search within the focused column; Enter accepts, Esc
cancels and clears that column's query.Muzical is organized as five top-level views. Switching views never interrupts playback.
1 Library — three-column artist → album → song browser with per-pane
search.2 Playlist — ad-hoc play queue built with space from the library
view.3 Now Playing — full-pane readout with track metadata and a live
progress bar.4 Soulseek — connect, search, and download tracks into musicDir.5 Config — live editor for config.json fields (music dir,
extensions, Soulseek credentials).The Soulseek and Config views own their own keyboard handling; global
bindings (other than 1–5 view switches and q to quit) are suspended while
either is active. The Now Playing view hides the bottom playback bar since
its own progress bar is already present.
| Mode | Key(s) | Action |
|---|---|---|
| Normal | 1–5 |
Switch view |
| Normal | ↑ ↓ / j k |
Move selection in focused list |
| Normal | PgUp / PgDn |
Page by visible list height |
| Normal | g / G |
Jump to first / last item |
| Normal | Enter / p |
Toggle playback for selected song |
| Normal | s |
Stop playback |
| Normal | + / = / - |
Raise / lower volume (5% steps) |
| Normal | / |
Start search in focused column |
| Normal | q / Esc |
Quit |
| Search | type | Append to query |
| Search | Backspace / Delete |
Delete last character |
| Search | ↑ ↓ |
Move selection while searching |
| Search | Enter |
Leave search mode (keep query) |
| Search | Esc |
Cancel search and clear the query |
| Key | Action |
|---|---|
← → / h l |
Focus previous / next column |
space |
Append focused artist/album/song to queue |
c |
Clear the focused column's search query |
| Key | Action |
|---|---|
space |
Remove the selected entry from the queue |
c |
Clear the entire playlist |
| Key | Action |
|---|---|
Enter / p / space |
Toggle playback |
s |
Stop playback |
+ / - |
Adjust volume |
| Mode | Key(s) | Action |
|---|---|---|
| Input | type | Append to query |
| Input | Backspace |
Delete last character |
| Input | Enter |
Run search |
| Input | Esc |
Return to results list |
| List | / / i |
Open search input |
| List | c |
Connect (or reconnect) |
| List | ↑ ↓ / j k |
Move selection |
| List | PgUp / PgDn |
Page through results |
| List | g / G |
Jump to first / last result |
| List | Enter / d / space |
Download the selected result |
| List | x |
Clear results |
| List | 1–5 |
Switch view |
| List | q |
Quit |
| Mode | Key(s) | Action |
|---|---|---|
| Nav | ↑ ↓ / j k |
Move between fields |
| Nav | Enter / i |
Begin editing the focused field |
| Nav | 1–5 |
Switch view |
| Nav | q / Esc |
Quit |
| Edit | type | Append to draft |
| Edit | Backspace / Delete |
Delete last character |
| Edit | Enter |
Save the draft and persist to disk |
| Edit | Esc |
Cancel edit, keep existing value |
The binary accepts global options that apply to every mode and a handful of
subcommands for non-interactive use. Run muzical --help (or muzical <subcommand> --help) for the authoritative list.
| Flag | Purpose |
|---|---|
-d, --music-dir <path> |
Override musicDir from config |
-e, --extension <ext> |
Add / override a song extension (repeatable) |
-p, --player <name> |
Force a backend (mpg123 / mpv / ffplay / vlc) |
-c, --config <path> |
Use a custom config.json location |
--no-clear-screen |
Do not clear terminal on start/exit |
-v, --version |
Print version and exit |
muzical — launch the TUI (default).muzical config — print the resolved configuration as JSON and exit.muzical player [--player <name>] [--all] — print the detected playback
backend (or check a specific one / list all candidates).muzical scan [--json] — scan the music library and print a summary
without launching the UI.muzical list <artists|albums|songs> [--artist ...] [--album ...] [--json]
— list library contents with optional filters.muzical
muzical --music-dir ~/Music --player mpv
muzical scan --json
muzical list artists
muzical list songs --artist 'Radiohead' --album 'OK Computer'
muzical player --all
The first available binary from this list wins (in order):
mpg123mpvffplayvlcInstall any one of them and ensure it is on your PATH, or use
--player <name> to force a specific backend. Arguments are chosen for quiet,
non-interactive playback suitable for a TUI (e.g. mpv with --no-video).
Per-backend volume handling:
mpv — initial --volume=<n>; while playing, volume changes are sent
over --input-ipc-server for instant updates without a restart.mpg123 — -f <scale> where scale is volume / 100 * 256.ffplay — -af volume=<ratio>.vlc — backend-level volume flags are not used; + / - still track
master volume state for display.load_config resolves config.json (or --config <path>),
merges in CLI overrides, and validates musicDir / songExtensions plus
optional Soulseek credentials.load_music_dir walks musicDir for matching extensions.load_library parses each file's metadata into in-memory
LibrarySong entries.App (src/app.tsx) composes StatusBar, one of the five
view components, PlaybackBar, and HelpBar; keyboard input for the
Library / Playlist / Now Playing views is centralized in use_app_input.src/views/)
library_view.tsx — three-column artist/album/song browser.playlist_view.tsx — single-column play queue.now_playing_view.tsx — full-pane readout with progress bar.soulseek_view.tsx — search/download UI backed by useSoulseek.config_view.tsx — live config.json editor, writes via writeConfig.resolvePlayer / detectPlayer picks a backend from
PLAYER_CANDIDATES; usePlayer spawns it for the selected file path and
forwards volume changes (including mpv IPC updates).pnpm install
pnpm dev # run from TypeScript source
pnpm build # TypeScript + bundled CLI + TypeDoc
pnpm test # Mocha
pnpm test:vitest # Vitest
pnpm lint # markdownlint (README) + ESLint
pnpm lint:eslint # ESLint only (src + package.json)
pnpm format # Prettier
Release and docs scripts are defined in package.json (prepare-release,
serve:docs, etc.) for maintainers.
TypeDoc output is produced under docs/ when you run pnpm build (or
pnpm build:docs). Serve locally:
pnpm serve:docs
Issues and pull requests are welcome at https://github.com/f3rnox/muzical/issues.
pnpm lint and pnpm test before opening a PR when you touch
code or this README.This project is licensed under the MIT License — see LICENSE.md.
package.json homepage)