Per-Character Spring Pop (逐字Q弹)
The per-character pop that makes one line feel like a title sequence
- image
- subtitle_data
- IMAGE
Every character individually springing into place, overshooting its size, then settling with a little rotation wobble - that's the CapCut-style "逐字Q弹" effect this node is named after, and it's the first one you'll want for anything that isn't plain talking-head subtitles. Titles, chapter headers, a single emphatic line you want the audience to actually read. If you need long-running conversational subtitles, the podcast scroller or four-line flip nodes are the better fit; SubtitleSpringPop is for the moments where one line has to land.
It renders in pure CPU via Pillow, zero VRAM, compositing onto whatever IMAGE frame sequence you feed it.
How it works
The effect is a per-character timeline. Each glyph runs through the same animation with a stagger: alpha fades in over the first 0.1s, scale pops via a spring curve over 0.4s, and rotation springs back to upright over 0.3s - with a fixed overshoot of 5° on top of whatever rotation_start you set. The stagger_delay shifts each character's start time, so the pop ripples left to right instead of firing all at once. Once a character's 0.4s is up it clamps to full size, upright, fully opaque, and stays there.
The readable version of all that: characters appear in sequence, each one bouncing past its final size and wobbling back to rest. Cute, and genuinely good for a beat of emphasis.
Inputs worth touching
The node's defaults are sensible, so you mostly adjust the flavor:
stagger_delay(0.05s) - how long between characters. Crank it up for a slow dramatic build, keep it low for a snappy pop.scale_overshoot(1.3) - how far past 100% each character bounces. 1.0 is a plain fade-in with no bounce; 1.5+ gets cartoonish fast.rotation_start(-15°) - the starting tilt each character springs from. Negative tips them left, positive right; 0 turns the wobble off entirely.fill_color_top/fill_color_bottom- the vertical gradient fill, white to gold by default. This is where you make it look expensive or cheap.stroke_width/stroke_color- outline, 4px by default. On bright footage, the stroke is what keeps the text readable.position_x/position_y- normalized position on the frame (0.5/0.8 puts it lower-center).
The text widget is required; srt_path and subtitle_data are optional inputs, and if you feed it SUBTITLE_DATA, the node animates each subtitle as it becomes active. Output is a single IMAGE - the composited frame sequence, ready for a save/video node.
Install
Same pack, same drill:
cd ComfyUI/custom_nodes
git clone https://github.com/liaowu-boos/ComfyUI-Subtitle-Effects.git
cd ComfyUI-Subtitle-Effects
pip install "Pillow>=9.0.0" "jieba>=0.42"
Or search liaowu-boos/ComfyUI-Subtitle-Effects in ComfyUI Manager and restart. The seven bundled fonts (Source Han Sans/Serif, LXGW WenKai, SmileySans, Inter, JetBrains Mono) show up in the font_name dropdown automatically; drop your own .ttf/.otf into fonts/ and restart to add more. No models, no GPU.
Common issues
- The whole line pops at once.
stagger_delaytoo small or your clip is too short to see the ripple - 0.05s at 30fps is roughly two frames per character, so a long line eats a lot of the entrance. Budget it againstfps. - Text is unreadable. A heavy
rotation_startmid-motion plus thin stroke makes glyphs smear. Jack the stroke width up or drop the rotation for anything below ~font_size 48. - Long videos balloon memory. CPU rendering is fine, but 720p/30fps runs about 95MB per second of frames - a minute is over 5GB of RAM. Render in segments rather than one marathon run.
One aside: because every character is a separate animated layer, very long lines with big fonts are the one place this node gets heavy. For a title line it's perfect; for a paragraph, use the scroller.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| text | STRING | Hello World | — |
| font_name | COMBO | 7 options: Inter-Regular.otf, JetBrainsMono-Regular.ttf, LXGWWenKai-Regular.ttf, NotoSerifSC-Regular.otf, SmileySans-Oblique.ttf, SourceHanSansSC-Regular.otf, +1 | |
| font_size | INT | 7212–300 | — |
| line_gap | INT | 100–100 | — |
| fps | FLOAT | 30.001–120 | — |
| position_x | FLOAT | 0.500–1 | — |
| position_y | FLOAT | 0.800–1 | — |
| stagger_delay | FLOAT | 0.050.01–0.5 | — |
| scale_overshoot | FLOAT | 1.301–2 | — |
| rotation_start | FLOAT | -15-45–45 | — |
| fill_color_top | STRING | #FFFFFF | — |
| fill_color_bottom | STRING | #FFD700 | — |
| stroke_width | INT | 40–30 | — |
| stroke_color | STRING | #000000 | — |
| shadow_offset_x | INT | 5-20–20 | — |
| shadow_offset_y | INT | 5-20–20 | — |
| shadow_color | STRING | #000000 | — |
| srt_pathopt | STRING | — | |
| subtitle_dataopt | SUBTITLE_DATA | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |