lyric-romanizer Pt. 1: Stop Reinventing the Wheel
OpenKara needed CJK lyric romanization. While researching, I found a ready-made engine inside the Spotify Karaoke Chrome extension. One GitHub Issue later, I extracted it into a standalone npm package — lyric-romanizer — supporting 12 native scripts and 4 API-based scripts.
OpenKara is an open-source karaoke app I’m building with Tauri 2 + React + TypeScript. The core feature is separating vocals from a track and displaying synchronized lyrics. Recently I’ve been working on: CJK Transliteration.
Karaoke is most popular in Chinese, Japanese, and Korean-speaking parts of Asia. Romanizing those lyrics sounds simple — but the ecosystem has plenty of libraries, each covering only a slice of the problem, and quality varies widely.
Research
Perplexity helped me survey the main options.
pinyin is the classic choice for Chinese, clean API, but no tone marks. pinyin-pro adds tones, but only covers Mandarin. kuroshiro handles Japanese (hiragana, katakana, romaji), but it’s a plugin system rather than a standalone library. transliteration and any-ascii go broad, but with lower quality or no language-aware rules.
Each library handles one piece. I wanted good quality across multiple languages, so I decided to build a routing layer.
An Unexpected Find
Just as I was about to build a routing aggregator, I came across a Chrome extension: Spotify Karaoke.
The extension adds karaoke-style lyrics to the Spotify desktop client. It supports three lyric modes: Original, Romanized, and Translated.
What caught my eye: it had a built-in romanization engine supporting 16 scripts — Japanese (kuroshiro + kuromoji), Mandarin (pinyin-pro), Korean, Cyrillic, Tamil, Thai, Hindi, and more. The approach was exactly what I had in mind: detect the script type, route to the appropriate library.
A Conversation
I filed a GitHub Issue with Spotify Karaoke author Harold Alan, proposing to extract the romanization engine as a standalone npm package.
A few days later he replied positively and invited a PR. I asked about structure: monorepo or polyrepo? His answer was direct: polyrepo, publish it yourself, maintain full control, and he’d pull it in as a dependency. That surprised me.
Shipping
I extracted the routing logic from Spotify Karaoke’s codebase and packaged it as a general-purpose npm module.
The result is lyric-romanizer — 12 native scripts, 4 API-based scripts. No need to know which library handles which script; it routes automatically.
Retrospective
Three things stood out.
Research is underrated. I spent more time researching than writing code — and it was worth it. Without finding Spotify Karaoke, I’d have built something nearly identical from scratch.
Reaching out is underrated. The default in open source is to fork and wait. Not everyone actively contacts the author and proposes mutual benefit. The recent OpenCLI incident is an imperfect but related example.
Vibe Coding has made reinventing the wheel nearly free. Stacks for indie hackers, boilerplate for every domain — now almost every area has a scattered set of uneven implementations. From a systems perspective, it’s a real waste of resources.