Save Clip
A music clip from stills that can't drift off the beat
- image
- audio
- tags
- video
- path
- report
Save Clip is the video half of Kinburg-Nodes' song suite - the sibling of Save Song, built to sit at the end of the pack's Siren chain, where ACE-Step-class audio models turn lyrics and a section plan into an actual track. You feed it a picture (or a whole batch of pictures) and that audio, and it writes an h264+AAC mp4 whose cuts land where the music changes. One image and a three-minute song gets you a single still held over the whole track; a batch of images gets you a slideshow whose timing follows the song.
It's one of those nodes that sounds trivial until you price out the obvious alternative. In plain ComfyUI, "image batch plus audio" means Create Video + Save Video, and those encode one frame per batch item. A three-minute song at 30 fps is a 5,400-frame batch - roughly 17 GB of tensor for a 1024×1024 picture before the encoder has even seen anything. Save Clip never builds that batch. It keeps at most a couple of prepared slides alive and feeds the encoder frame by frame through PyAV (av, which already ships inside ComfyUI), so its memory footprint is set by the frame size, never by the song's length.
How the timing works
Here's the part worth understanding before you touch it: the audio's real length is the video's length, and the plan only supplies proportions. A Siren plan is written in bars ("Chorus | Nina | 8 bars"), and bars are only seconds once you know a tempo that lives on some other node - the moment the two disagree, your pictures slide off the music. So the plan is read for its ratios and scaled so the last slide ends on the last sample. No BPM to wire, no drift, and if the plan came out 5% short, the stretch factor is printed in the report instead of silently wrong. Leave plan empty and it just splits the song evenly.
The inputs that actually matter:
- image and audio are the required pair. Nothing resamples in time - the batch is the list of pictures, not the video's frames. Four images over a three-minute song is four shots, not four frames.
- layout - "one slide per section" (the default), "by section label" (every row called Chorus gets the same slide, so the chorus shot comes back like it does in a cut music video), or "even" (ignore the plan).
- crossfade (default 0.5s, centred on the cut) and ken_burns (off by default) are the only things that actually move. Ken Burns makes every frame unique, which grows the file and encode time several times over - with it on, raise fps to 24+ or the motion steps instead of flows.
- lyrics with their
[Verse 1]-style markers writes an .srt next to the mp4, timed to each section - but only when a Siren plan is wired, because the lyrics' markers and the plan's labels are matched on the same words. - tags takes a
SONG_TAGSbundle (the same nodeSave Songeats) and writes it into the mp4's own metadata.
You get three outputs: video (a VIDEO object on newer ComfyUI builds, so the clip can chain into ComfyUI's own video nodes; on older builds the file is simply on disk), path (where it landed), and report (what happened, section by section).
Installing it
It's one of 98 nodes in the Kinburg-Nodes pack, so you install the whole thing:
cd ComfyUI/custom_nodes
git clone https://github.com/Kinburg/Kinburg-Nodes
or search "Kinburg-Nodes" in ComfyUI Manager. One gotcha: the pack's install.py (which Manager runs automatically) installs the CUDA build of llama-cpp-python for its LLM nodes - you don't need that for Save Clip, but it's part of a normal install, so don't panic at the wait. Save Clip's own needs - av, Pillow, numpy - are already in ComfyUI.
Where people get burned
The batch-isn't-frames confusion is the big one. Ken Burns left on at 12 fps looks steppy; crank the fps. A .srt that doesn't appear almost always means the lyrics' markers and the plan's labels don't literally match - wire both from the same Siren node. An empty audio input fails loudly with "there is nothing to time the slides to." And odd source dimensions get rounded down to even sides, because yuv420p demands it. The timing logic is the strongest part of this node - trust the audio, not your plan's bar count.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | The picture, or the batch of slides. One image = one still behind the whole song; a batch = a slideshow, laid out by 'layout' below. Nothing is resampled in time: the batch is NOT the video's frames, it is the list of pictures. A 4-image batch over a 3-minute song is four shots, not four frames. | |
| audio | AUDIO | The song. Its length IS the video's length — the slides are scaled onto it, so the last picture always ends on the last sample. | |
| filename_prefix | STRING | clips/clip | Save path prefix under ComfyUI/output. A counter is appended; the subtitles (.srt) share the same base name. |
| quality | COMBO | Balanced (crf 21) | h264 + AAC in an mp4, at one of three CRFs. 'High' is visually lossless on a still and roughly doubles the file; 'Small' is what survives a messenger's own re-encode. |
| planopt | STRING | Where the pictures change. Two shapes are read: • Siren Score's / Siren Cast's plan — 'label | voice | 16 bars' rows. The section LABELS come with it, which is what makes 'by section label' possible. • Orpheus's 'durations' — a plain comma list of seconds, already cut to the music. Only the PROPORTIONS are used: the plan is scaled onto the real length of the audio, so no bpm is needed here and the slideshow cannot drift off the song. Leave it empty to split the song evenly. | |
| layoutopt | COMBO | one slide per section | How the slides are handed to the plan's sections. • one slide per section — the picture changes on the section boundary. More slides than sections and the extra ones subdivide the longest sections; fewer and they cycle. • by section label — every row called 'Chorus' gets the SAME slide, so the chorus shot comes back the way it does in a cut video. Costs nothing: the labels are already in the plan. • even — the plan is ignored and the song is split equally. |
| fpsopt | INT | 121–60 | Nothing moves in a slideshow, so this is mostly file size and encode time — 12 is plenty for stills and a third of the frames of 30. Raise it to 24-30 when 'crossfade' or 'ken_burns' is on: those are the only things here that actually move, and at 12 fps a dissolve steps rather than flows. |
| frame_sizeopt | COMBO | source (from the image) | The video's frame. 'source' takes the first image's own size (rounded down to even sides, which yuv420p requires). The rest are the shapes the platforms want; how a picture that isn't that shape is laid onto them is 'fit'. |
| fitopt | COMBO | pad with a blurred copy | What to do when the picture and the frame are different shapes. • pad with a blurred copy — the picture whole, on a blurred enlargement of itself. The usual choice for a square cover in a 16:9 frame. • pad with black — the picture whole, on black. • crop to fill — no bars, but the edges of the picture are gone. |
| crossfadeopt | FLOAT | 0.50–4 | Seconds of dissolve at each change of picture, centred ON the cut — half before, half after — so the moment the two pictures are equal is the moment the music turns. 0 = hard cuts. Clamped to 40% of the shorter neighbour, so a long fade cannot swallow a short section. |
| ken_burnsopt | FLOAT | 0.000–0.4 | Slow zoom and drift over each slide, as a fraction (0.08 = the picture creeps 8% closer across its section). A still held for three minutes reads as a broken video; this is what makes it read as a shot. It is off by default because it is not free: every frame becomes unique, so the file and the encode time grow several times over. With it on, use 24 fps or more. |
| lyricsopt | STRING | The lyrics, with their '[Verse 1 - ...]' markers — the SAME text that went to Siren. Each section's own lines are written to a .srt next to the video, timed to that section's segment. Only works with a Siren plan wired (the labels are what the two are matched on). Sections with no sung lines get no cue. | |
| tagsopt | SONG_TAGS | Artist, album, year, genre — wire a 'Song Tags' node, the same one Save Song takes. Written into the mp4's own metadata; the title falls back to the file's name. | |
| verboseopt | BOOLEAN | true | Print the report to the console. The same text is always on the 'report' output. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | — |
| path | STRING | — |
| report | STRING | — |