π Save Video (grade) - LD
The end-of-render node that stops AI video looking like AI video
- images
- images_b
- audio
- audio_b
- path
The last node in a video workflow is usually a boring Save Video: frames in, mp4 out. Save Video (grade) - LD is the opinionated version. It's the pack's finishing node - category LD / PromptMaster / Finishing - and it bundles the two things that separate a render you'd post from a render you'd delete: a grade you can preview instantly, and an encoder step that's been tuned against real measurements rather than vibes. It's an output node, so it sits at the end of the rail.
What it actually does
Executing the node stages a temporary preview in its own panel - instant, because the preview is CSS filters over the frames rather than an encode. Pressing SAVE is what writes a real file to ComfyUI/output/, and that save goes through ffmpeg (system ffmpeg if you have it, else the one bundled with ComfyUI), which is where the actual pixels get decided. save_master (on by default) writes the master to output/ on every run, so this node can be your only save node and nothing is lost when a tab closes.
It takes two render rails because the pack runs two different samplers: images + audio for single-shot video (T2V / I2V / REF), and images_b + audio_b for chained H3 Master output (MULTISHOT / MV). Lazy evaluation only pulls whichever rail is actually live, so a dead sampler never gets flattened. Output is a single path string pointing at what you saved.
The controls that earn their keep
sharpen(0β1, default 0) - contrast-adaptive sharpening. VAE decode is soft; 0.3β0.5 is plenty, and CAS leaves contrasty edges alone so you don't get white halos around hairlines.grain(0β60, default 0) - film grain, applied after sharpening so it's never sharpened into crawling noise. 4β10 is subtle, 20+ is a look. This is the classic anti-"AI look" move, and the KB's own corpus shows it's folk-standard practice for a reason.crf(default 19) - x264 quality, where lower is cleaner and bigger. The tooltip carries the author's actual measurements, and they're the most useful thing on this node: grain is where size goes, because random noise is incompressible - at crf 17, a grain-8 clip cost 98Γ the no-grain size; at crf 20 it was 13Γ. So 15β17 for clean plates with no grain, 19β20 for normal, 22+ when size matters more than the last of the detail.encoder-h264(plays anywhere),h265(same look, ~30% smaller), orav1(smallest, and the only 10-bit option a browser actually plays; slower to encode).bit_depthstays at 8, and that's a decision with a reason: 10-bit H.264 measures better on paper but doesn't play in Discord, browsers or the Windows player, and a master nobody can open isn't a master.interp_mult- this one trips people up. It does not interpolate; if a RIFE node upstream doubled your frames, you set this to the multiplier so the file is written at the doubled fps - smoother instead of 12 seconds of slow-motion with the audio adrift.scale- a Lanczos resample of the saved master. It's a resize, not an upscaler (no invented detail), but it can't flicker the way a per-frame neural upscaler can. For real added detail the tooltip's advice is blunt and right: run SeedVR2 upstream and leave this at 1.loudnorm- EBU R128 to β16 LUFS, the streaming target. H3's re-synthesis and a muxed original MP3 land at very different levels, so without it a run of takes jumps in volume.mux_mp3- filename inComfyUI/inputof the original track. When set, the saved file carries that audio instead of H3's re-synthesis, at offset zero, because the picture was generated against that exact waveform.
Install and the honest catches
cd ComfyUI/custom_nodes
git clone https://github.com/Lora-Daddy/PromptMasterLD
# restart ComfyUI
It needs an ffmpeg binary somewhere reachable - ComfyUI ships one via imageio, so usually you're fine; if you see "no ffmpeg found", install one on PATH or pip install imageio-ffmpeg into ComfyUI's python. The one real footgun: mux_mp3 only lands right when the chain's frame maths is correct - a clip that ships fewer frames than its window skips the song at every join, and no fixed offset fixes that. Keep save_master on, read the preview as an approximation (CSS has no gamma or color temperature, so close, not exact), and trust SAVE for the real thing.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| fps | FLOAT | 24.001β120 | β |
| filename_prefix | STRING | LD_VIDEO/take | β |
| crf | INT | 190β51 | x264 quality. LOWER is cleaner and bigger. 19 is the default because GRAIN is where the size goes: random noise is incompressible, so at 17 the encoder spends near-lossless bitrate preserving it. Measured on a 768x928 clip, grain 8 cost 98x the no-grain size at crf 17, and 13x at crf 20. 15-17 clean plates with no grain - 19-20 normal - 22+ when size matters more than the last of the detail. |
| imagesopt | IMAGE | VIDEO β single-shot frames (T2V / I2V / REF). | |
| images_bopt | IMAGE | CHAIN β H3 Master frames (MULTISHOT / MV). | |
| audioopt | AUDIO | VIDEO β single-shot audio. | |
| audio_bopt | AUDIO | CHAIN β H3 Master audio. | |
| mux_mp3opt | STRING | Filename in ComfyUI/input of the ORIGINAL track. When set, the saved file carries this audio instead of H3's re-synthesis Γ’β¬β the MP3 went in as <Audio 1> to drive the mouth, so the picture was made against this exact waveform and it lands at offset zero. Only true when the chain's frame maths is right: a clip that ships fewer frames than its window skips song at every join, and no fixed offset can put that back. Measured flat to ΓΒ±0.10s across 40s once it is. | |
| save_masteropt | BOOLEAN | true | ON: every run writes the master to output/ as well as the preview, so this node can BE your Save Video and nothing is ever lost to a closed tab. SAVE in the panel still writes a separate graded take beside it. OFF: the old behaviour - temp preview only, nothing lands until you press SAVE. |
| bit_depthopt | STRING | 8 | Kept at 8. 10-bit measured better on paper - 71 gradient levels against 18 - but H.264 High 10 does not play in Discord, the Windows player or any browser, and a master you cannot send anyone is not a master. encode() still takes bit_depth=10 if a use for it turns up. |
| encoderopt | STRING | h264 | h264 - universal, plays anywhere. h265 - same look, roughly 30% smaller. av1 - smallest, and the ONLY 10-bit option a browser will actually play. Slower to encode. crf below is in x264 units; the other two are converted, not reused. |
| sharpenopt | FLOAT | 0.000β1 | Contrast-adaptive sharpening (cas). VAE decode is soft; this lifts flat detail and leaves contrasty edges alone, so no white halos around hairlines. 0.3-0.5 is plenty. 0 = off. |
| grainopt | FLOAT | 00β60 | Film grain, applied AFTER sharpening so it is never sharpened into crawling noise. Breaks up the plastic look of flat areas and hides what is left of any banding. 4-10 is subtle, 20+ is a look. 0 = off. |
| interp_multopt | STRING | 1 | Set this to whatever a RIFE / frame-interpolation node upstream multiplied the frames by. It does NOT interpolate - it fixes the fps so the result is SMOOTHER instead of SLOWER. RIFE 2x on 24fps/6s gives 288 frames. Written at 24fps that is 12 seconds of slow motion with the audio adrift; written at 48fps it is the same 6 seconds, twice as smooth. Leave at 1 when nothing is interpolating. |
| scaleopt | STRING | 1 | Lanczos resample of the SAVED master. This is a resize, NOT an upscaler - it invents no detail. What it buys is that it cannot flicker: every frame is resampled identically, which a per-frame neural upscaler cannot promise. With sharpen 0.3-0.4 a 1.5x delivery bump holds up well. For REAL added detail run SeedVR2 upstream instead - you have it installed - and leave this at 1. |
| loudnormopt | BOOLEAN | false | EBU R128 to -16 LUFS, the streaming target. H3's re-synthesis and a muxed MP3 land at very different levels; without this a run of takes jumps in volume. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| path | STRING | β |