Nodes/msch-comfyui-nodes/🎀 Lyric Sync β€” Caption Overlay
ComfyUI Node

🎀 Lyric Sync β€” Caption Overlay

Karaoke captions that actually match the song β€” bilingual, timed, and tasteful

By mariobillyΒ·Created a day agoΒ·Updated a day agoΒ· 0
🎀 Lyric Sync β€” Caption Overlay
  • images
  • timing
  • images
β—„frame_rate30.000β–Ί
β—„bilingualtrueβ–Ί
β—„positionbottomβ–Ί
β—„font_pathC:\Windows\Fonts\segoeui.ttfβ–Ί
β—„font_size0β–Ί
β—„text_color#FFFFFFβ–Ί
β—„box_color#000000β–Ί
β—„box_opacity0.60β–Ί
β—„corner_radius18β–Ί
β—„padding24β–Ί
β—„y_offset0β–Ί
β—„max_width_pct0.80β–Ί
β—„fade_ms150β–Ί
β—„hold_ms0β–Ί
β—„highlight_wordsfalseβ–Ί
β—„highlight_color#FFDC50β–Ί

Burning lyrics onto a music video by hand - timing each line to the audio in an editor - is the kind of job that eats an afternoon. LyricSyncOverlay (display "🎀 Lyric Sync - Caption Overlay") exists to kill that step: you give it the video frames and a word-accurate timing track, and it draws each lyric line in a rounded box exactly when the singer sings it. It's part of the MSCH Lyric Sync family in the MSCH Nodes pack, and it's the boring-in-a-good-way half of the two lyric renderers: no glitchy text rain, just clean, subtitle-tool-grade captions.

How it works

The trick is that this node doesn't do any timing work itself - it consumes a LYRIC_TIMING value that comes from the pack's 🎀 Lyric Sync - Align node upstream. That Align node takes your audio plus the pasted lyrics (one line per on-screen box) and builds a timeline of when each line and word starts and ends. Overlay just reads that timeline: at frame index ÷ frame_rate, it shows whichever line is current, fading it in/out over fade_ms and holding it for hold_ms past its end if you want captions to linger.

Where this gets good is bilingual (on by default). The Align node accepts an optional parallel translation - one line per original lyric line - and Overlay stacks the translation under the original, handling right-to-left text shaping for languages like Arabic or Hebrew. That single toggle is the difference between a Western-only tool and something you can caption a global release with.

The inputs that matter

  • images + timing are the required pair: your frames and the LYRIC_TIMING from Align.
  • frame_rate must match the frame rate of your video - get this wrong and captions drift off the vocals. Align already knows the audio; Overlay needs you to tell it the visual rate.
  • position (bottom/center/top), font_size (0 = auto from frame width), max_width_pct, padding, corner_radius and box_opacity shape the caption box. y_offset nudges it vertically, which is how you clear a watermark or a logo.
  • highlight_words turns word-by-word highlighting on (karaoke fill), tinted by highlight_color. This only works as well as your alignment, though - see below.

Wiring it

The chain is: LyricSyncAlign (audio + lyrics) β†’ LyricSyncOverlay (video frames) β†’ Save Video. The node outputs images only; it deliberately does not attach audio, so connect the soundtrack separately to your encoder and let the encoder mux it. font_path defaults to a Windows font path (segoeui.ttf), which is a great default on Windows and a guaranteed error on Linux/macOS - point it at any .ttf you have.

Install

Lyric Sync ships inside the unified MSCH Nodes pack (35 nodes, one install). In ComfyUI Manager search for "MSCH Nodes" / msch-comfyui-nodes, or:

cd ComfyUI/custom_nodes
git clone https://github.com/mariobilly/msch-comfyui-nodes.git
cd msch-comfyui-nodes
python -m pip install -r requirements.txt

then restart ComfyUI. For word timing, the Align node can call the WhisperX CLI for a word-level timeline - that's optional, and the pack has a documented even-timing fallback if WhisperX isn't installed, so line captions work either way. Word highlighting is only as accurate as your alignment method.

Gotchas

The most common complaint pattern is captions drifting off the vocals, and nine times out of ten it's frame_rate not matching the video. Second, remember the node knows nothing about your audio - a video at 25 fps needs frame_rate 25, not whatever the nearest integer happens to be. And don't chase word-perfect highlighting before you've confirmed line timing feels right: if the lines land, words are a refinement; if the lines drift, words will only broadcast the drift louder.

CategoryLyricSync

Inputs (18)

NameTypeDefaultDescription
imagesIMAGEβ€”
timingLYRIC_TIMINGβ€”
frame_rateFLOAT30.0001–240β€”
bilingualBOOLEANtrueβ€”
positionCOMBObottom3 options: bottom, center, top
font_pathSTRINGC:\Windows\Fonts\segoeui.ttfβ€”
font_sizeINT00–4000 = auto from frame width
text_colorSTRING#FFFFFFβ€”
box_colorSTRING#000000β€”
box_opacityFLOAT0.600–1β€”
corner_radiusINT180–200β€”
paddingINT240–200β€”
y_offsetINT0-2000–2000β€”
max_width_pctFLOAT0.800.2–1β€”
fade_msINT1500–2000β€”
hold_msINT00–3000Keep each line on screen this long past its end.
highlight_wordsBOOLEANfalseβ€”
highlight_colorSTRING#FFDC50β€”

Outputs (1)

NameTypeDescription
imagesIMAGEβ€”