Lyrics
Nokkvi shows lyrics over the Queue view’s cover art. Synced lyrics follow the playing track line by line. Plain (untimed) lyrics from your server display too, drifting with playback instead of pretending to a timing they don’t have. Nothing is faked either way.
Turning it on
Section titled “Turning it on”Lyrics are off by default. The Lyrics row under Settings → Playback, right after Crossfade, has three choices:
- Off: no lyrics, and nothing is looked up.
- On: lyrics over the cover for every song. A song without lyrics shows “No lyrics for this track”.
- Auto: lyrics only for songs that have them. Every song you play while the Queue or Theater Mode is open is looked up, and the cover stays plain until a song’s lyrics turn up, so a song without any never shows an empty overlay.
Two quicker switches sit outside Settings:
- The player bar: the Lyrics toggle (captions icon) sits rightmost in the mode-toggle row, next to the visualizer. It’s the first control to fold into the ⋮ overflow when the window narrows.
- The
Lhotkey: Toggle Lyrics.
In Off and On they flip between the two. In Auto they leave the setting alone and act on the playing song only: they hide lyrics Auto is showing, or show the “No lyrics” overlay on a song without any, and the next song is Auto’s call again. The button is lit while lyrics are on screen.
The choice is saved as lyrics_enabled in config.toml (false, true or "auto"), so it survives restarts.
Where lyrics come from
Section titled “Where lyrics come from”When a track starts, Nokkvi resolves its lyrics through four sources in order and stops at the first hit:
| Order | Source | When it’s used |
|---|---|---|
| 1 | Your own files — ~/.local/share/nokkvi/lyrics/, outside .cache/ | Always checked first. A hit never touches the disk cache or the network. |
| 2 | Your server — OpenSubsonic Song Lyrics (getLyricsBySongId) | Only when the server advertises the songLyrics extension. |
| 3 | Cached LRCLIB downloads — ~/.local/share/nokkvi/lyrics/.cache/ | Always checked, even with online fetch off: the download already happened. |
| 4 | LRCLIB — lrclib.net | Only when lyrics_fetch_online is on (the default). |
Your server outranks LRCLIB, including copies already cached on disk. A Navidrome answer is your own library talking — a sidecar file you put there, or the lyrics embedded in the track’s own tags — so it wins even when it is plain untimed text and the LRCLIB copy is synced.
The LRCLIB lookup is an exact match on artist, title, album, and duration — never a fuzzy search — so a hit is never-wrong by construction. LRCLIB matches the album letter for letter, so when it has nothing timed under your album (a deluxe edition, say), Nokkvi asks once more without the album; the artist, title and length still have to match. Timed lyrics win, and when LRCLIB only has untimed text for the song, that shows instead. Tracks LRCLIB doesn’t know simply show no lyrics rather than someone else’s.
When lrclib.net is too busy to answer, Nokkvi waits the second or so it asks for and tries again, for up to about 12 seconds, before giving up until the song plays again.
Timed lyrics from LRCLIB are written into the store’s .cache/ folder as a tagged .lrc file, so the next play of that track resolves offline. That’s the only thing ever written: untimed LRCLIB text and a “not found” are remembered for the session, not recorded on disk, so a song can pick up timed lyrics once someone adds them to LRCLIB. Lyrics added by a library rescan clear that memory, so they show up without restarting Nokkvi.
Embedded lyrics
Section titled “Embedded lyrics”Nokkvi streams over HTTP and never opens your audio files. It doesn’t need to: Navidrome reads the embedded lyrics tags at scan time and serves them through the same getLyricsBySongId endpoint above. That covers ID3v2 USLT and SYLT frames on MP3, plus the lyrics / unsyncedlyrics properties on FLAC, Ogg and MP4. A SYLT frame, or a USLT holding LRC text, arrives as a synced sheet; anything else arrives as plain text.
Two things worth knowing about how Navidrome serves them:
- One source wins per song. Navidrome’s
LyricsPrioritydefaults to.ttml,.yaml,.yml,.elrc,.lrc,.srt,.txt,embedded, and the first source with a result wins.embeddedis last, so a sidecar.lrcsitting next to the audio file hides the embedded tag. That’s a server setting, not something Nokkvi controls. - Blank lines are dropped. Navidrome strips empty lines out of plain lyrics before sending them, so stanza breaks are gone by the time Nokkvi sees the sheet. Bracketed lines like
[Chorus]survive and render as written.
Staying offline
Section titled “Staying offline”Turn Lyrics: Online Fetch off (Settings → Playback) and resolution uses only your own files, your own server, and LRCLIB downloads already cached on disk — no third-party requests at all. Independently of that, lookups only run while the Queue view or Theater Mode is on screen, and a short debounce means skipping through the queue never fires a request per skip.
Adding your own lyrics
Section titled “Adding your own lyrics”Drop .lrc files anywhere under ~/.local/share/nokkvi/lyrics/ — subfolders are fine. Everything outside the store’s own top-level .cache/ folder counts as yours, and yours are checked before anything else, so a sheet you add always beats an LRCLIB copy of the same song. Files are matched by the tags inside them, never by filename:
[ar:Artist]and[ti:Title]are required for a file to be indexed.[al:Album]and[length:mm:ss]are optional but break ties when several sheets match.
Matching tries the exact artist + title first (case-insensitive). If that misses, a looser pass forgives featured-artist credits, trailing qualifiers like (Live) or (Remix), and punctuation differences — but it refuses to guess when the result would be ambiguous. A wrong synced sheet is worse than none.
Standard LRC works as you’d expect: [mm:ss.xx] timestamps, several timestamps sharing one line, and an [offset:] tag. A file with no timestamps at all is treated as no match: plain lyrics come from your server or a live LRCLIB answer, never from disk, so the store stays synced-only.
Reading the overlay
Section titled “Reading the overlay”A synced sheet centers its active line, inked in the theme accent, with neighbors fading by distance. The column glides between lines with eased motion and snaps on seeks. Before the first timestamp the sheet waits dimmed on its first line; after the last one, the final line stays lit to the end of the track.
A plain sheet has no line known to be current, so none is highlighted. A flat band of full-brightness lines carries the eye instead, with the same falloff outside it, and the column drifts from the first line to the last in step with playback progress. There’s no easing: a seek moves the sheet in one step, the way a seek moves the progress bar. If the track’s length isn’t known the sheet simply holds still.
Legibility is worked for, not assumed: every glyph carries a soft dark halo and a scrim dims the art behind the words, so lines read over any cover — white ink over white printed type included. With crossfade on, the outgoing track’s sheet dissolves out as the next one fades in, and the next sheet is prefetched near the end of the current track so sequential transitions never show a blank gap. The over-cover visualizer keeps playing beneath the words instead of yielding to them.
Scrolling a plain sheet. Drift is a guess, so you can override it: roll the mouse wheel over the cover and the sheet moves two lines a notch, then keeps drifting from wherever you left it. It stops cleanly at the first and last line, and the scroll resets when the track changes.
Apart from that one gesture the overlay is hands-off. Clicks, the right-click artwork menu, and the wheel over a synced sheet all pass through to the panel beneath, exactly as if the lyrics weren’t there.
If nothing matched anywhere, the panel says “No lyrics for this track”. The message is a settled verdict, not a guess: it appears only once the lookup has finished, so it never flashes while one is in flight. The fix is one of the sources above — drop an .lrc in the store, serve lyrics from your server (embedded tags included), or leave online fetch on.
Cover blur
Section titled “Cover blur”If sharp art behind the words is still too busy, Lyrics: Cover Blur (Settings → Playback) frosts the playing track’s cover while lyrics display — set lyrics_backdrop_blur:
| Value | Look |
|---|---|
off (default) | The cover stays sharp under the lyrics scrim |
light | A gentle soften — detail stays readable through the frost |
medium | A true frosted-glass read — shapes survive, detail goes |
heavy | Melts the cover to a color wash |
The blur is computed once per track on a downscaled copy and cached — it’s a one-shot job, not a per-frame effect, so it costs nothing while playing.
Alignment
Section titled “Alignment”Lyrics: Alignment (Settings → Playback) sets where the lines sit across the cover, on the Queue and in Theater Mode alike. Set lyrics_alignment:
| Value | Lines |
|---|---|
left | Every line starts at the left edge |
center (default) | Each line is centered |
right | Every line ends at the right edge |
A line too long for one row wraps, and its second row follows the same alignment. The “No lyrics for this track” note and the word-by-word highlight follow the alignment too.