Karaoke-style word-by-word lyrics in a floating Liquid Glass window and live in the menu bar, with built-in translation into 43 languages. Free, open source, no account needed.
Word-by-word glow · live Persian translation |
Despacito — Spanish lyrics · live English translation |
🎬 Full promo videos: Spanish → English (Despacito) · English → Persian (Beat It)
brew install --cask ateymoori/tap/lyricglowHomebrew (recommended — signed & notarized, zero warnings):
brew install --cask ateymoori/tap/lyricglowOne-line installer (no Homebrew needed):
curl -fsSL https://raw.githubusercontent.com/ateymoori/lyricglow/main/scripts/install.sh | bashRuns on: macOS 11.0+ · Apple Silicon (M1–M4) and Intel · needs the Spotify or Apple Music desktop app · internet for lyrics (cached content works offline)
Manual installation & first-run permission
- Download from Releases:
LyricGlow-arm64.dmg(Apple Silicon) orLyricGlow-x64.dmg(Intel) - Open the DMG → drag to Applications
- Open it — since v0.7.0 the app is code-signed and notarized (Developer ID: Royan AB), so macOS launches it without warnings
Old unsigned builds (v0.6.1 and earlier) need one command if macOS refuses to open them:
xattr -cr /Applications/LyricGlow.appAutomation permission: LyricGlow reads the current track with AppleScript, so macOS asks for Automation access on first run. If you dismissed the prompt, the app shows a one-time explainer with an Open Settings button, or grant it manually in System Settings → Privacy & Security → Automation → LyricGlow → enable Spotify / Music. The app detects the grant and resumes automatically — no restart needed.
The app runs as a menu bar agent (
LSUIElement) — no Dock icon, no application menu.
- Translation is built in. Every lyric line can show a live translation into any of 43 languages — including full RTL rendering for Persian, Arabic and Hebrew. Most lyrics apps charge a subscription for this.
- Lyrics live in your menu bar. The current line ticks by next to the clock, so the window can stay hidden while you work.
- Private by design. No account, no analytics, no tracking. Lyrics, artwork and translations are fetched from open sources and cached on your disk.
- Light on the battery. Polling is adaptive, UI updates are event-scheduled, and rendering stops completely when nothing is playing.
- Works with both Spotify and Apple Music — detection is local, via AppleScript.
|
Lyrics
|
Playback & Metadata
|
App
|
Data sources: synced lyrics from LRCLIB, artist metadata from TheAudioDB, top tracks/albums from the Spotify Web API (optional login), translations from Google Translate's web endpoint with Lingva Translate as automatic fallback. Everything is cached on disk for 7 days by default.
All sections visible |
Compact — metadata hidden |
Lyrics only |
RTL support |
Layouts are built from the Display settings tab — each section can be toggled independently.
Live lyrics in the menu bar — works with the window hidden
- Launch LyricGlow from Applications — it lives in the menu bar
- Play music in Spotify or Apple Music
- Watch lyrics sync word-by-word in the window and next to the tray icon
- Customize via the gear icon, or the menu bar icon → Settings
Window controls & shortcuts
| Action | Result |
|---|---|
Cmd+L (global) |
Toggle the floating window on/off |
| Click menu bar icon | Show / focus the window (also re-enables it after ×) |
| Drag the window background | Move the window (frameless, resizable, always-on-top) |
| Click / drag the progress bar | Seek to that position |
| Click the line above / below | Jump to that lyric |
| Click album art / vinyl | Open the track in Spotify (Spotify playback only) |
| Document button (next to ▶) | Open the full lyrics modal (Esc closes it) |
| Click any line in the full lyrics | Jump to that point in the song |
× button |
Hide the window and turn off Show Window |
| Floating Lyrics (menu bar or Settings) | Turn the window into a bare lyric line on the desktop |
The full lyrics view follows the current line on its own. Scrolling it yourself takes over, and a Resume auto-scroll pill appears to hand control back. Clicking a line also resumes following.
Like a desktop lyric overlay: only the current line, large and centred, with the word-by-word glow and the translation underneath. No window, no controls, no background — clicks pass straight through to whatever is behind, and the line floats above full-screen apps on every space.
To move it, choose Move Floating Lyrics in the menu bar: the bar becomes draggable and outlines itself. Drag it where you want, then choose Lock Floating Lyrics to make it click-through again and remember the spot. Floating and normal layouts each remember their own position and size.
While no line is playing (instrumental passages, or a track with no lyrics) the overlay draws nothing at all rather than parking a status message on your desktop. Opening Settings returns the window to the normal layout, since the settings panel does not fit in the slim bar.
Menu bar menu: Show Window · Show Tray Lyrics · Floating Lyrics · Move/Lock Floating Lyrics · Settings · Check for Updates · Quit
Settings & where your data lives
| Tab | Contents |
|---|---|
| General | Launch at login, menu bar lyrics, Floating Lyrics, translation on/off + target language, Spotify login/logout |
| Display | Show/hide each UI section, reset to defaults |
| Cache | Total size, per-entry list with delete, clear all |
| Logs | Log file count/size, open the logs folder, clear logs |
| Data | Path |
|---|---|
| Settings | ~/Library/Application Support/LyricGlow/config.json |
| Cache (lyrics, metadata, images, translations) | ~/Library/Application Support/LyricGlow/.cache |
| Logs | ~/Library/Logs/LyricGlow/main.log |
Spotify tokens are encrypted with the macOS Keychain-backed safeStorage API before being stored.
| Symptom | Fix |
|---|---|
| "Permission Required" in the window | Grant Automation access (see Install) |
| Window never appears | Menu bar icon → Show Window, or press Cmd+L |
| No lyrics for a track | LRCLIB has no synced lyrics for it; only synced ([mm:ss.xx]) lyrics are shown |
| Spotify login does nothing | SPOTIFY_CLIENT_ID is missing from .env (source builds only) |
| Top tracks / albums stay empty after login | Spotify requires the app owner to hold Premium; the log shows one warning and the app pauses Spotify requests for 30 min. Artist data still loads from TheAudioDB |
| Stale artwork or metadata | Settings → Cache → delete the entry or Clear All |
| App won't open after download | Only old unsigned builds (v0.6.1 and earlier): xattr -cr /Applications/LyricGlow.app. v0.7.0+ is notarized and opens directly |
Prerequisites: macOS, Node.js 22 (the version used in CI), npm.
git clone https://github.com/ateymoori/lyricglow.git && cd lyricglow
npm install
cp .env.example .env # optional: needed only for Spotify login
npm start # build and launch the appCI runs npm ci, npm run typecheck and npm run build on macos-latest for every push and PR.
Environment variables
.env is optional and read from the project root at runtime. Only two keys are used:
| Key | Purpose |
|---|---|
SPOTIFY_CLIENT_ID |
Enables "Login with Spotify" (top tracks & albums). Create an app at the Spotify dashboard and add musicdisplay://callback as a redirect URI. Note: Spotify's current policy requires the app owner's account to hold an active Premium subscription before the Web API answers, and apps in development mode only accept users added in the dashboard. |
CACHE_DURATION_HOURS |
Cache lifetime in hours (default 168 = 7 days). |
.env is bundled into the packaged app, so build your DMG after creating it if you need
Spotify login in a self-built release.
One extra launch-time variable: LYRICGLOW_CAPTURE=1 starts the window on the normal layer
instead of always-on-top, so screen recorders and Zoom/Meet window pickers can see it —
macOS hides overlay windows from window capture. Used for demo recordings and screen sharing.
Scripts & releasing
| Command | What it does |
|---|---|
npm run dev |
electron-vite dev mode (rebuilds main/preload on change) |
npm run build |
Build main, preload and renderer into dist/ |
npm start |
build + launch Electron |
npm run typecheck |
tsc --noEmit for the node and web tsconfigs |
npm run lint / npm run format |
Biome check / format ./src |
npm run quality |
knip (dead code) + jscpd (duplication) + Biome |
npm run dist:mac |
Signed DMGs for arm64 and x64 in release/ |
npm run release |
Full release pipeline: version bump, signed DMGs, notarization + stapling, git tag, GitHub release, Homebrew cask update |
Releasing (maintainers): add a ## [x.y.z] section to CHANGELOG.md, then run
npm run release (or -- minor / -- major). The script checks the tree is clean and in sync,
bumps the version, builds both DMGs signed with the Royan AB Developer ID, notarizes and staples
them with Apple, then tags, publishes the GitHub release, and updates the
Homebrew cask. One-time setup: the Developer ID
certificate in the Keychain, a notarytool profile named lyricglow-notary, and an
authenticated gh CLI.
Project structure & architecture
src/
├── main/ # Electron main process
│ ├── index.ts # Lifecycle, window, tray, AppleScript polling, IPC handlers
│ ├── auth/ # Spotify PKCE OAuth (token storage + refresh)
│ ├── managers/ # Lyrics, TheAudioDB, Spotify metadata, translation,
│ │ # unified cache, image cache, update check
│ └── translation/ # Provider interface + Google Web / Lingva chain
├── preload/index.ts # contextBridge API exposed as window.musicAPI
├── renderer/index.ts # UI logic: sync manager, displays, metadata, settings
└── shared/utils/ # Logger, SecureFetch (verified HTTPS), LrcParser
resources/ # index.html + styles.css loaded by the renderer
build/ # App icon, tray icons, DMG background, entitlements
scripts/ # install.sh (end users), release.sh (maintainers)
How it fits together: the main process runs a cached AppleScript through osascript on an
adaptive interval (1.8 s while playing, 3 s paused, 5 s idle, with an immediate poll after any
transport action), broadcasts track changes over IPC, and fetches lyrics/metadata in parallel.
The renderer interpolates position between polls for smooth progress and word glow, and stops
rendering while paused; the menu bar line is scheduled to update exactly when the next lyric
line is due, so it stays correct even when the window is hidden — at near-zero idle cost.
PRs welcome! Priority areas:
- Windows/Linux support (music detection is macOS/AppleScript-only today)
- Additional lyrics sources
- Automated tests
- UI translations
Please run npm run typecheck and npm run lint before opening a PR.
| Service | Purpose |
|---|---|
| LRCLIB | Synchronized lyrics |
| TheAudioDB | Artist metadata, biography, images |
| Spotify API | Top tracks & albums, artist details |
| Google Translate / Lingva | Lyrics translation (43 languages, with fallback) |
| Vazirmatn | Font for Persian/Arabic lyrics |
MIT License © AmirHossein Teymoori
If LyricGlow makes your music better, a ⭐ helps others find it.