Skip to main content

Cross-platform terminal lyrics visualizer and LRC/WLRC toolkit (MPRIS · Windows SMTC · macOS)

Project description

lyricsooo

Live, synced song lyrics — rendered as block letters right in your terminal.

CI License: MIT Python Platform

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

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.
Terminal lyric visualizer
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.
Phrase-level lyric rendering
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.
Glitch track switching
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.
Album-art colour tinting
Animated Ad-Break Screen

Detects when Spotify plays an advertisement and shows an animated idle card, resuming lyrics immediately on the next song.
Animated ad break screen

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 effectsstandard (instant), typewriter (char-by-char with a blinking cursor), fade (soft per-line fade-in) and glow (the active line gently breathes). Pick with --reveal, or open the themed --select picker 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): follow pywal, caelestia, matugen, a fixed:#rrggbb accent, or file:PATH to 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-player to 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-cli helper; 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-cli artwork); 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lyrics_tool-0.2.2.tar.gz (94.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lyrics_tool-0.2.2-py3-none-any.whl (84.7 kB view details)

Uploaded Python 3

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

Hashes for lyrics_tool-0.2.2.tar.gz
Algorithm Hash digest
SHA256 acff49d45be798317ed8478fe0d730096a064d5149bfccb41c1770011ff6b906
MD5 fdecef44ca7ec21f2212308500d3e0b1
BLAKE2b-256 730d8ace9b4b52c17911253b5aef28c9d08ce73d8e1250ad6721d8241c0f4e0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for lyrics_tool-0.2.2.tar.gz:

Publisher: release.yml on hamza-abdelmoumene/lyrics-tool

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for lyrics_tool-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c7b535fbcd9bdbb6171e7c1b5d01d3d79c8a11f47c01a7b68a2afeb901093ec9
MD5 4c441f7a0d711119fc938f2cdf41fb35
BLAKE2b-256 821348cbb0c7fbd7e92096e920b88c8aac52f4867cef1330a81b3f5412066790

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page