Parsing SRT subtitles in the browser: the 30-line converter that makes every WebVTT player happy
Disclosure: I'm building RKMovies — an ultra-lightweight 4K media player client (0.91 MB APK, hardware-accelerated decoding, zero telemetry). Site: https://rkmovies.dpdns.org · Android APK: https://rkmovies.dpdns.org/apk · Windows app: https://rkmovies.dpdns.org/desktop
Every video player on Android that is worth using speaks WebVTT. Almost every subtitle file floating around the internet is SRT. They look identical at a glance, but the parsers do not forgive the differences.
The gap is small and annoying:
- SRT uses a comma in timestamps (
00:00:01,500), WebVTT demands a dot (00:00:01.500) - SRT cues start with an optional numeric index line, WebVTT does not have one
- SRT files often ship with a BOM and CRLF line endings
So instead of pulling in a subtitle library (remember: the whole APK is 0.91 MB, every dependency is a size audit), I wrote a tiny converter that runs in the browser when a local subtitle file is loaded:
async function fileToVtt(file) { let t = await file.text(); t = t.replace(/\r/g, "").replace(/^/, ""); if (/^\s*WEBVTT/.test(t)) return t; const out = []; for (const b of t.split(/\n{2,}/)) { const lines = b.split("\n").filter(l => l.trim() !== ""); if (/^\d+$/.test(lines[0]?.trim())) lines.shift(); const tl = lines.shift(); if (!tl || tl.indexOf("-->") < 0) continue; out.push(tl.replace(/,/g, ".") + "\n" + lines.join("\n")); } return out.length ? "WEBVTT\n\n" + out.join("\n\n") + "\n" : ""; } The whole thing is ~20 lines. It reuses the same cleanup pass we already run for inline captions (stripping tags like <i> and positioning junk), then hands the result to the player as a Blob URL. From the user's side: pick an .srt file, it just works.
Two edge cases that actually bit me in testing:
- Some SRT files use LF only, some CRLF, some have a BOM. Normalizing all three up front saves a "why are my subtitles empty" bug report.
- Multi-line cues must stay multi-line. If you flatten them, dialogue and sound effects merge into one unreadable line.
If you are shipping a small player and want subtitles without a dependency, this is the whole trick. WebVTT is forgiving once the commas become dots.
RKMovies runs this on Android (0.91 MB APK with hardware decoding and multi-audio support: https://rkmovies.dpdns.org/apk) and on Windows (https://rkmovies.dpdns.org/desktop). No account, no telemetry.
