Windows-first local lyrics server that reads the active media session through SMTC, serves a browser lyrics UI, and streams now-playing updates over WebSocket.
This project combines:
- A Node.js server built with Hono
- A Windows SMTC bridge written in C#/.NET
- A browser UI in
public/for live lyrics and now-playing display - Lyrics lookup and caching powered by
lrclib.net - On-demand romanization for Japanese, Korean, and Chinese lyrics
- Detects the current system media session on Windows via
GlobalSystemMediaTransportControlsSessionManager - Exposes current song metadata through HTTP and WebSocket
- Fetches synced or plain lyrics from
lrclib.net - Caches lyrics to disk
- Serves a local lyrics page at
/lyrics - Includes a
/welcomeflow backed by sample data - Proxies album artwork through
/api/v1/image-proxy
- Windows 10 or Windows 11
- Node.js 18+
- npm
- .NET 8 SDK for local helper builds
- 7-Zip for packaged
.7zbuilds
Notes:
- The SMTC bridge is Windows-specific and the main app is designed around it.
npm run devexpects the helper to be built locally.
server.js: Node entrypoint, HTTP routes, static serving, WebSocket server, SMTC helper process managementlib/lyrics.js: lyrics fetching, normalization, caching, romanizationlib/image-proxy.js: artwork proxy and in-memory image cachepublic/: browser UI assetstools/smtc-bridge-cs/: C# SMTC bridge projectscripts/build-smtc.js: Windows packaging script
npm installBuild the Windows SMTC helper and start the server:
npm run devThen open:
http://127.0.0.1:1312/lyricshttp://127.0.0.1:1312/welcome
Create a bundled Windows executable:
npm run buildUseful variants:
npm run build:x64
npm run build:gui
npm run build:bundleBuild outputs are written to dist/.
Returns the latest active media session snapshot.
Example response:
{
"videoId": "stable-song-id",
"title": "Song Title",
"artist": "Artist Name",
"album": "Album Name",
"elapsedSeconds": 42.1,
"songDuration": 215.5,
"isPaused": false,
"imageSrc": "data:image/png;base64,...",
"sampledAtMs": 1710000000000
}Query params:
videoIdrequiredartistrequiredtitlerequiredalbumoptionaldurationoptional
Example:
/api/v1/lyrics?videoId=abc123&artist=Utada%20Hikaru&title=First%20Love&album=First%20Love&duration=249
Fetches and caches remote artwork by URL.
Returns sample song, lyrics, and audio payloads used by the welcome flow.
Connect to:
ws://127.0.0.1:1312/ws
Observed message types:
song_updatedsong_progressplayback_updatedlyrics_updated
Common runtime variables:
PORT: server port, default1312PUBLIC_DIR: override static asset directorySMTC_HELPER_PATH: explicit path tolyrics-smtc-bridge.exeLYRICS_DIR: lyrics cache directory, defaultC:/tmp/lyrics
Image cache tuning:
IMAGE_CACHE_TTL_MSIMAGE_MAX_SIZE_BYTESIMAGE_CACHE_MAX_ENTRIESIMAGE_CACHE_MAX_MEMORY_BYTESIMAGE_CACHE_CLEANUP_INTERVAL_MS
Romanization tuning:
ROMANIZATION_IDLE_UNLOAD_MSROMANIZATION_UNLOAD_IMMEDIATELYROMANIZATION_FORCE_GC_AFTER_UNLOADROMANIZATION_INMEMORY_MAX_ENTRIES
Logging and cache throttling:
LOG_REPEAT_TTLMAX_RECENT_LOG_KEYSLYRICS_CACHE_HIT_TTLMAX_RECENT_LYRICS_CACHE_HITS
- The server starts the SMTC bridge automatically on boot.
- If the helper executable is missing, the server logs the expected output path and the
dotnet publishcommand needed to build it. - When packaged, the helper is embedded and extracted to a temp directory at runtime.
- CORS is enabled for all routes.
npm run dev: build helper, then startserver.jsnpm run build:smtc:helper: publish the C# SMTC bridge forwin-x64npm run build: bundle and package the Windows appnpm run build:gui: package without console windownpm run build:bundle: createbundle.jswith esbuild
No license file is currently included in this repository.