Cross-platform terminal lyrics visualizer and LRC/WLRC toolkit (MPRIS · Windows SMTC · macOS)
Project description
lyrics-tool is a small, cross-platform suite that fetches synced lyrics,
prepares them into phrase- or word-level timing, and renders them live
in the terminal as block letters, synchronized to whatever your media player is
playing.
It follows your player natively on Linux (MPRIS / playerctl), Windows
(System Media Transport Controls) and macOS (nowplaying-cli) — one code
path, one snapshot, the right backend picked automatically for your OS. Works
with Spotify and any local player (mpv, VLC, rhythmbox, Apple Music, …);
auto-follows whatever is playing, or pin it with --player.
Contents
- Quick start
- The three commands
- Previews
- Features
- Platform support
- Installation
- Player backends
- Colour sources
- Usage
- How your lyrics are stored
- Configuration
- Troubleshooting
- Footprint
- Development & contributing
- Uninstall
Quick start
Once installed (see Installation), there is nothing to configure — every command falls back to a shared default location under your data directory, so the whole flow works with zero flags:
lyricsooo-fetch --audio-dir ~/Music # 1. download synced lyrics for your library
lyricsooo-cook # 2. prepare them (split long lines, etc.)
lyricsooo # 3. play — synced to your current track
Or skip steps 1–2 entirely: just run lyricsooo and start playing music.
Missing lyrics are fetched and cached on the fly as each track plays.
lyricsooo # just works — fetches lyrics live as songs play
The three commands
| Command | What it does |
|---|---|
lyricsooo |
Live, terminal block-letter visualizer synced to the active player. |
lyricsooo-fetch |
Batch-download synced lyrics from LRCLIB / syncedlyrics. |
lyricsooo-cook |
Split long phrases; optionally convert to word-level (.wlrc). |
Every command supports --help.
Previews
Rendered straight from the real engine — regenerate any time with
python tools/generate_previews.py.
| Feature & Description | Visual Preview |
|---|---|
| Terminal Lyrics Visualizer The core visualizer rendering block-letter lyrics in the terminal. Custom fonts, dynamic resizing, ambient floating notes, and real-time player synchronization. |
|
| Phrase-Level Playback Tracks the playing song line by line, matching vocal delivery. A phase-locked clock cancels player lag so lines land with the vocal. |
|
| Glitch Track Announce Every track change opens with a short glitch burst — band tears, scrambling letters, chromatic flicker — that resolves into the album info card. |
|
| Dynamic Colour Tinting Extracts the dominant colour from the album artwork and tints the now-playing card and lyrics to match the song — or follow your desktop theme instead. |
|
| Animated Ad-Break Screen Detects when Spotify plays an advertisement and shows an animated idle card, resuming lyrics immediately on the next song. |
Features
- Cross-platform live sync — follows your player on Linux, Windows and macOS through one pluggable backend layer; the right one is auto-selected. See Platform support.
- Phrase and word-level sync —
.lrc(per line) and.wlrc(per word). - On-the-beat timing — a phase-locked playback clock continuously eases onto
the player's timeline and cancels reported-position lag, so lines land with
the vocal, not behind it. Nudge live with
-/+(saved), or set--offset. - Four line-reveal effects —
standard(instant),typewriter(char-by-char with a blinking cursor),fade(soft per-line fade-in) andglow(the active line gently breathes). Pick with--reveal, or open the themed--selectpicker to choose effect + style before you play. - Never blocks on the network — lyrics for the playing track are fetched in the background, so the display stays responsive and track switches register instantly. An animated finding lyrics screen shows while it searches; a calm no synced lyrics screen if a song genuinely has none.
- Universal colour sources — by default the card is painted in the album
cover's dominant colour and the lyrics tinted with a softer accent, but the
source is pluggable (
--color-source): followpywal,caelestia,matugen, afixed:#rrggbbaccent, orfile:PATHto track any JSON palette live. - Auto-follow any player — Spotify and local players out of the box; pin with
--player spotify/mpv. Prefers whatever is actually playing, and skips web browsers and chat/telephony apps by default, so a YouTube lecture or a Telegram voice message can't hijack the lyrics (--ignore-playerto customise). - Glitch track announce + ad-break screen + floating music notes —
ambient polish that keeps the view alive; disable with
--no-notes. - Responsive, diffed renderer — block letters wrap to fit and fall back to plain text when the window is tiny; repaints only when something changes, so a held line costs ~no CPU and never flickers.
- Offline word mode and custom fonts (supply your own block font via JSON).
Platform support
| Platform | Live visualizer (lyricsooo) |
Offline tools (-fetch / -cook) |
Backend |
|---|---|---|---|
| Linux / BSD | ✅ full (art + local files) | ✅ | playerctl (MPRIS) |
| Windows 10/11 | ✅ | ✅ | smtc (System Media Transport Controls, [windows] extra) |
| macOS | ✅¹ | ✅ | nowplaying-cli |
| WSL | ✅ (as Linux) | ✅ | playerctl |
The backend is auto-detected; override with --player-backend or
$LYRICSOOO_PLAYER_BACKEND. A truecolor, UTF-8 terminal is recommended
everywhere (Kitty, Alacritty, WezTerm, Windows Terminal, iTerm2, …); on Windows
the console is switched into ANSI/VT mode automatically.
¹ macOS live sync depends on the
nowplaying-clihelper; on macOS 15.4+ Apple restricted the underlying framework, so availability can vary by OS version and player. Album-art tinting works on Linux (MPRIS art URL) and macOS (nowplaying-cliartwork); on Windows it's best-effort from the SMTC thumbnail. If a track exposes no art, colour falls back to none — a theme source (--color-source pywal/caelestia/fixed:#…) always works.
Installation
Universal — works on every OS and distro
lyrics-tool is a pure-Python package, so pipx (or uv) is the
simplest install anywhere:
# From PyPI
pipx install lyrics-tool # …or: uv tool install lyrics-tool
# Bleeding edge — straight from GitHub main
pipx install "git+https://github.com/hamza-abdelmoumene/lyrics-tool.git"
Then open a new terminal and run lyricsooo --help. The lyricsooo,
lyricsooo-fetch, and lyricsooo-cook commands are now on your PATH.
Optional system dependencies
Only needed for the corresponding feature; the tool degrades gracefully without them.
Linux — live sync + audio durations
# Debian / Ubuntu
sudo apt install -y playerctl ffmpeg pipx && pipx ensurepath
# Arch
sudo pacman -S --needed playerctl ffmpeg python-pipx && pipx ensurepath
# Fedora
sudo dnf install -y playerctl ffmpeg pipx && pipx ensurepath
playerctl powers the live visualizer; ffmpeg (ffprobe) reads audio
durations when processing.
Windows — native live sync
Install the System Media Transport Controls backend with the [windows] extra:
pipx install "lyrics-tool[windows]"
# or from a local clone: pipx install ".[windows]"
That pulls in winsdk. No playerctl needed — it reads Windows' global media
session directly. WSL users can instead follow the Linux steps for the full
MPRIS experience.
macOS — live sync helper
brew install nowplaying-cli # enables live sync
brew install ffmpeg # optional, for audio durations
pipx install lyrics-tool
Distro packages
| Method | Command |
|---|---|
| Arch (AUR) — latest release | yay -S lyrics-tool (or paru, or makepkg -si on packaging/aur) |
Arch (AUR) — build from main |
yay -S lyrics-tool-git |
From source (development)
git clone https://github.com/hamza-abdelmoumene/lyrics-tool.git
cd lyrics-tool
pip install -e '.[dev]'
Optional — high-accuracy word timing. Per-word onset detection uses
librosa(heavy, opt-in) — add the[onset]extra:pipx install "lyrics-tool[onset]".
Player backends
lyricsooo reads the active player through the backend that fits your OS. It's
picked automatically, but you can force one:
lyricsooo --player-backend playerctl # Linux / MPRIS
lyricsooo --player-backend smtc # Windows media session
lyricsooo --player-backend nowplaying-cli # macOS
export LYRICSOOO_PLAYER_BACKEND=smtc # …or pin it in your shell rc
The active backend is shown in the startup banner. If your platform has no
backend available, the offline lyricsooo-fetch / lyricsooo-cook tools still
work and the visualizer shows its idle screen with a hint. Adding a new platform
is a self-contained change — see CONTRIBUTING.
Colour sources
Where the lyric tint and now-playing card get their colour is fully pluggable.
Pick a source with --color-source (or set $LYRICSOOO_COLOR_SOURCE):
| Source | Colours follow | Needs |
|---|---|---|
art (default) |
the current track's album art | Pillow + a player/OS that exposes art (Linux, macOS; best-effort on Windows) |
none |
terminal default foreground | — |
pywal |
~/.cache/wal/colors.json |
you already run wal/pywal |
caelestia |
the Caelestia desktop scheme | Caelestia |
matugen |
~/.cache/matugen/colors.json |
matugen |
fixed:#RRGGBB |
one static accent you choose | — |
file:PATH |
any JSON palette (configurable key map) | — |
lyricsooo --color-source pywal # match your wal palette, live
lyricsooo --color-source fixed:#89b4fa # one fixed accent (great on Win/macOS)
export LYRICSOOO_COLOR_SOURCE=caelestia # …or make it the default everywhere
Theme-file sources update live — retheme your desktop and the lyrics recolour under your feet, no restart.
Usage
# 1. Fetch lyrics for your library
lyricsooo-fetch --audio-dir ~/Music
# 2. Prepare: split long phrases (add --wlrc for word-level)
lyricsooo-cook --no-require-audio
# 3. Visualize, synced to the current track
lyricsooo # phrase mode
lyricsooo --wlrc # word mode
lyricsooo --reveal glow # breathing active line
lyricsooo --player spotify # pin to one player
lyricsooo --select # pick effect + style interactively
Useful flags: --player <name> to pin a player, --player-backend <name> to
force a backend, --reveal {standard,typewriter,fade,glow}, --select for the
picker, --color-source <src>, --no-cover-color / --no-notes to strip
effects, --offset <sec> to nudge sync (positive = earlier), --banner-hold <sec> for the title-card dwell. Press Ctrl+C to exit.
How your lyrics are stored
Unless you pass explicit --*-dir flags, everything lives under one root
(honouring $XDG_DATA_HOME, or the platform equivalent):
~/.local/share/lyrics-tool/
└── lyrics/
├── raw/ # lyricsooo-fetch downloads here → lyricsooo-cook's input
└── processed/ # lyricsooo-cook writes here → lyricsooo reads here
Live-nudged sync offsets are kept separately under ~/.local/state/lyrics-tool/.
These directories are created automatically — you never have to mkdir
anything.
Configuration
Processing, visualizer and colour defaults can be set in a YAML file (see
src/lyrics_tool/config_example.yaml) and
passed with --config path/to/config.yaml. CLI flags override the file.
Troubleshooting
| Symptom | Cause & fix |
|---|---|
lyricsooo: command not found |
Your pipx/uv bin dir isn't on PATH. Run pipx ensurepath, then open a new terminal. |
| "no live-sync backend available" note | Install the backend for your OS: playerctl (Linux), pipx install 'lyrics-tool[windows]' (Windows), brew install nowplaying-cli (macOS). |
| Lyrics don't move / no sync | A player must be running. Check the startup banner's backend line; try pinning with --player spotify. |
no synced lyrics for a track |
That song has no synced lyrics on LRCLIB — playback continues normally. |
| Block letters look like boxes / no colour | Use a truecolor, UTF-8 terminal (Kitty, Alacritty, WezTerm, Windows Terminal, iTerm2, …). |
| Cover tint missing | Some tracks/players expose no art (esp. streamed audio on Windows). Use --color-source fixed:#RRGGBB or a theme source (pywal/caelestia/matugen) instead. |
ffprobe: not found when processing |
Install ffmpeg, or run lyricsooo-cook --no-require-audio to skip duration lookups. |
Footprint
It's light — a sleep-driven loop, not a busy renderer. The diff renderer only repaints when the lyric line, the notes, or the terminal size actually change, and playback position is extrapolated from the monotonic clock instead of polling the player every frame.
Measured on Linux / CPython 3.14, one lyricsooo process during continuous
playback:
| Metric | Idle / paused | 80×24 terminal | Large terminal (≈200×50) |
|---|---|---|---|
| Memory (RSS) | ~30 MiB | ~33 MiB | ~33 MiB |
| CPU | ~0% | ~1% of one core | ~3% of one core |
CPU scales with terminal size and --refresh-rate; memory is flat. --no-notes
trims steady-state CPU further. Numbers are approximate and hardware-dependent.
Development & contributing
pip install -e '.[dev]'
pytest # full suite — no playerctl, audio, or network needed
ruff check . # lint
mypy src # types (informational)
The test suite drives the visualizer loop with a stubbed player and unit-tests every OS backend through fakes, so it runs identically on Linux, Windows and macOS. See CONTRIBUTING.md for the full workflow and how to add a player backend, SECURITY.md for the security model and reporting, and CHANGELOG.md for release notes. By participating you agree to the Code of Conduct.
Uninstall
pipx uninstall lyrics-tool
rm -rf ~/.local/share/lyrics-tool ~/.local/state/lyrics-tool # cached lyrics (optional)
Credits
Forked from tacos-terminal-lyrics; restructured into an installable,
cross-platform package with a pluggable backend layer, a responsive/diffed
renderer, offline word-mode, and a test suite.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lyrics_tool-0.2.2.tar.gz.
File metadata
- Download URL: lyrics_tool-0.2.2.tar.gz
- Upload date:
- Size: 94.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acff49d45be798317ed8478fe0d730096a064d5149bfccb41c1770011ff6b906
|
|
| MD5 |
fdecef44ca7ec21f2212308500d3e0b1
|
|
| BLAKE2b-256 |
730d8ace9b4b52c17911253b5aef28c9d08ce73d8e1250ad6721d8241c0f4e0e
|
Provenance
The following attestation bundles were made for lyrics_tool-0.2.2.tar.gz:
Publisher:
release.yml on hamza-abdelmoumene/lyrics-tool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lyrics_tool-0.2.2.tar.gz -
Subject digest:
acff49d45be798317ed8478fe0d730096a064d5149bfccb41c1770011ff6b906 - Sigstore transparency entry: 2227411327
- Sigstore integration time:
-
Permalink:
hamza-abdelmoumene/lyrics-tool@f2e9b32d3fcbb7f0714136671edfd00aaef65776 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/hamza-abdelmoumene
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f2e9b32d3fcbb7f0714136671edfd00aaef65776 -
Trigger Event:
push
-
Statement type:
File details
Details for the file lyrics_tool-0.2.2-py3-none-any.whl.
File metadata
- Download URL: lyrics_tool-0.2.2-py3-none-any.whl
- Upload date:
- Size: 84.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7b535fbcd9bdbb6171e7c1b5d01d3d79c8a11f47c01a7b68a2afeb901093ec9
|
|
| MD5 |
4c441f7a0d711119fc938f2cdf41fb35
|
|
| BLAKE2b-256 |
821348cbb0c7fbd7e92096e920b88c8aac52f4867cef1330a81b3f5412066790
|
Provenance
The following attestation bundles were made for lyrics_tool-0.2.2-py3-none-any.whl:
Publisher:
release.yml on hamza-abdelmoumene/lyrics-tool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lyrics_tool-0.2.2-py3-none-any.whl -
Subject digest:
c7b535fbcd9bdbb6171e7c1b5d01d3d79c8a11f47c01a7b68a2afeb901093ec9 - Sigstore transparency entry: 2227411711
- Sigstore integration time:
-
Permalink:
hamza-abdelmoumene/lyrics-tool@f2e9b32d3fcbb7f0714136671edfd00aaef65776 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/hamza-abdelmoumene
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f2e9b32d3fcbb7f0714136671edfd00aaef65776 -
Trigger Event:
push
-
Statement type: