Skip to content

matejnovak/phosphor

Repository files navigation

█▀█ █░█ █▀█ █▀ █▀█ █░█ █▀█ █▀█
█▀▀ █▀█ █▄█ ▄█ █▀▀ █▀█ █▄█ █▀▄

A CRT-glow terminal music player with six scope modes, a Game of Life that pulses with the beat, and a tiny mascot that blinks at you.

License: MIT OR Apache-2.0 Made with Rust Platform: macOS · Linux


phosphor is a single-binary terminal music player written in Rust. It plays your local library, draws the audio in real time as one of six scope modes, and keeps everything (theme, scope, library path, volume, repeat, shuffle) in a small TOML file so the next run picks up where you left off.

┌──────────────────────────────────────────────────────────────────────────┐
│  █▀█ █░█ █▀█ █▀ █▀█ █░█ █▀█ █▀█                                          │
│  █▀▀ █▀█ █▄█ ▄█ █▀▀ █▀█ █▄█ █▀▄  v0.4.0  Theme {PHOSPHOR}     ▟▀▀▀▙     │
│                                                                ▐ ▘ ▝ ▌    │
│                                                                ▝▀▀▀▘     │
├──────────────────────────────────────────────────────────────────────────┤
│ AUTECHRE   Gantz Graf  (Gantz Graf)   320kb/s  44100Hz   2:14 / 4:18     │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━◆──────────────────────────────────────────  │
├──────────────────────────────────────────────────────────────────────────┤
│ ┌─ ART ──┐ ┌─ PLAYLIST (12) ────────┐ ┌─ VU ┐ ┌─ SPECTRUM ───────────┐  │
│ │ ▓▓▓▓▓▓ │ │ ▶ Gantz Graf           │ │ ▌▌  │ │  ▄▆█ ▆ ▅ ▄▃▂▁        │  │
│ │ ▓▓██▓▓ │ │   Outpt                │ │ ▌▌  │ │ ▆██▇ ▇ ▇ ▆▅▄▃▂▁      │  │
│ │ ▓▓▓▓▓▓ │ │   Cipater              │ │ ▌▌  │ │ ███▇ █ ▇ ▆▅▄▃▂▁      │  │
│ └────────┘ └────────────────────────┘ └─────┘ └──────────────────────┘  │
├──────────────────────────────────────────────────────────────────────────┤
│ [PLAY]  track:1/12   shuf:OFF   rep:OFF   vol: 80%   scope:SPECTRUM      │
│ [TRACK] AUTECHRE . Gantz Graf                                            │
└──────────────────────────────────────────────────────────────────────────┘

Features

  • Plays mp3, wav, flac, ogg, m4a, aac from a folder (recursively) or a single file.
  • Six scope modes, switched live with Tab:
    • WAVEFORM . classic L/R oscilloscope.
    • VECTORSCOPE . X-Y stereo phase, 45° rotated.
    • SPECTRUM . live FFT bars, log-warped frequency axis.
    • SPECTROGRAM . scrolling time-vs-frequency heat map.
    • MID/SIDE . (L+R)/2 over (L-R)/2, useful for mastering.
    • LIFE . Conway's Game of Life seeded from audio transients.
  • Album-art panel that renders embedded cover art using half-block () pixels for square-ish 2:1 cells.
  • Stereo VU meter with peak hold.
  • Eight built-in themes: PHOSPHOR, AMBER, MATRIX, VAPOR, ICE, BLOOD, INK, SUNSET. Cycle with t.
  • Settings persist to ~/.config/phosphor/config.toml (debounced, 1s).
  • In-app library swap: press l, type a path (~/ expands), Enter.
  • Mac media-key bindings (F7 to F12).
  • A subtle reactive mascot in the banner that fades in on activity and flashes on strong audio transients.
  • ~30 fps while audio plays, ~5 fps while paused. No needless wakeups.
  • Single static binary, no daemon, no IPC, no config wizard.

Install

phosphor needs a recent Rust toolchain (1.85+, edition 2024). Any 24-bit-color terminal with a Unicode font will render correctly. Tested on macOS Terminal, iTerm2, Alacritty, kitty, WezTerm, and GNOME Terminal.

From source

git clone https://github.com/matejnovak/phosphor.git
cd phosphor
cargo build --release
./target/release/phosphor ~/Music

Or install the binary into your Cargo bin path:

cargo install --path .
phosphor ~/Music

One-shot run

cargo run --release -- ~/Music

Usage

phosphor [OPTIONS] [PATH]

PATH is a music file or a directory walked recursively. With no argument, phosphor re-opens the library from the previous run (saved in the config), falling back to the current directory.

phosphor                              # resume last library
phosphor ~/Music                      # play a folder
phosphor album.flac                   # play one file
phosphor --theme MATRIX --scope life  # specific look + scope
phosphor --shuffle --repeat all       # party mode
phosphor --list-themes                # print theme names
phosphor --help                       # full flag list

Flags

Flag What it does
--theme <NAME> Initial theme. See --list-themes.
--scope <MODE> Initial scope: waveform, vectorscope, spectrum, spectrogram, midside, life.
--no-vu Hide the VU meter for this run.
--no-scope Hide the scope panel for this run.
--no-art Hide the album-art panel for this run.
--shuffle Start with shuffle on.
--repeat <MODE> off, all, or one.
--volume <PERCENT> Initial volume, 0 to 150.
--list-themes Print all theme names and exit.
--no-config Ignore the saved settings file at startup.
--ephemeral Don't save changes from this run.

Keys

Playback

Key Action
Space play / pause
n / p next / previous track
/ seek -10s / +10s
Shift + ← / seek -5s / +5s (fine)
/ navigate playlist
Enter play selected track
+ / - volume up / down
m mute toggle
s shuffle toggle
r cycle repeat (off, all, one)

Mac media keys

Key Action
F7 / F8 previous / play-pause
F9 next track
F10 mute
F11 / F12 volume down / up

View

Key Action
Tab cycle scope mode (six modes)
v toggle VU meter
c toggle scope panel
a toggle album art
t cycle theme
l load a different music library
? / F1 toggle the help overlay
Ctrl+C quit

Library swap

Press l to open the path prompt, type or edit a path (~/ expands to $HOME), then Enter to load. Esc cancels. The chosen folder is remembered for next time and walked recursively for supported formats.

Themes

Name Mood
PHOSPHOR green CRT, default
AMBER early-80s amber monitor
MATRIX brighter green-on-green
VAPOR magenta + cyan synthwave
ICE cool blue
BLOOD red on red
INK grayscale, near-monochrome
SUNSET warm orange + pink

Cycle live with t or pin one with --theme NAME.

Configuration

phosphor writes a single TOML file at ~/.config/phosphor/config.toml. All keys are optional; missing keys preserve defaults. Saves are debounced (at most one write per second, plus one final flush at exit). Example:

theme = "MATRIX"
scope_mode = "spectrum"
show_scope = true
show_vu = true
show_album_art = true
shuffle = false
repeat = "all"
volume = 80
muted = false
library = "~/Music"

If the file is malformed at startup, phosphor backs it up to config.toml.bak and continues with defaults instead of refusing to run.

Use --no-config to ignore the saved file for one run, or --ephemeral to run normally but skip writing changes back to disk.

How it works (brief)

rodio + symphonia decode and play audio. Stereo samples are tapped into a small ring buffer through audio::TapSource while passing through to the sink, so the visualizations don't affect playback in any way. The run-loop draws at ~30 fps while audio is playing, drops to ~5 fps when paused, and performs a single event::poll per frame so input stays responsive without busy-spinning.

For a deeper walkthrough (run-loop, persistence, scope mode anatomy, how to add a key/theme/scope), see ARCHITECTURE.md.

Tests

cargo test

Every leaf module has its own #[cfg(test)] mod tests covering its public surface: parsers, formatters, theme lookup, settings round-trip, audio buffer cap, DSP edge cases, Game of Life invariants, audio-ext detection, peak meter behaviour, transport mute round-trip, artwork cache invariants.

Compatibility

  • Requires a Unicode-aware, 24-bit-color terminal. Most modern terminals qualify (macOS Terminal, iTerm2, Alacritty, kitty, WezTerm, GNOME Terminal, Windows Terminal).
  • Cover art needs a font with half-block glyphs (, ); the default font on the terminals above ships them.
  • Mac media keys (F7-F12) only do anything if your terminal forwards them to the foreground app rather than handing them to the system. iTerm2 and Alacritty pass them through; Apple's Terminal generally does not.

Contributing

Issues and pull requests welcome. The codebase aims for one obvious place to do each thing (one source of truth for status messages, one parser per enum, one renderer per scope), so when in doubt, look for the existing pattern in ARCHITECTURE.md before adding a parallel one.

cargo fmt and cargo clippy --all-targets should pass cleanly. cargo test should stay green; if you change behaviour that has a test, update the test.

License

Licensed under either of

at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

Single-binary terminal music player with oscilloscope, vectorscope, spectrum, spectrogram, mid/side and Game of Life scopes. Written in Rust.

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages