Nodes/ComfyUI-Rennart/🎞️ Video Crossfade (Rennart)
ComfyUI Node

🎞️ Video Crossfade (Rennart)

Dissolve between two clips without leaving the node graph

By Rennart2025·Created 14 days ago·Updated a day ago· 2
🎞️ Video Crossfade (Rennart)
  • video_a
  • video_b
  • images
  • count
transition_frames10
modelinear
offset0

So you've generated clip A, generated clip B, and now you want the classic A-then-B edit with a soft dissolve where they meet. The honest default is exporting both to a video editor and doing it there. This node exists for the times you'd rather not - it takes two sequences of frames and stitches them together with a crossfade entirely inside ComfyUI, in tensor space. No encoder, no editor, no leaving the graph.

It's the lone video node in ComfyUI-Rennart, a MIT-licensed grab-bag of small utilities (color wheels, palettes, date strings, a random number node) that Rennart2025 mostly builds for their own workflows. Several nodes in the pack trace back to WAS Node Suite; this one is original, and it's only a few hundred lines of torch math. That's not a knock. Sometimes a utility that does one boring job well beats another 2,000-line "complete video suite."

What it actually does

The key mental shift: in ComfyUI, a "video" is just an image batch - a tensor whose first dimension is the frame count, each slice an IMAGE. This node never touches a video file. Feed it video_a and video_b (both IMAGE batches) and it hands back one longer batch.

Under the hood (create_crossfade in Rennart_Video_Crossfade.py), the output is three chunks concatenated:

  • the front of clip A, up to transition_frames frames before its end,
  • a blend zone where the last frames of A and the first frames of B are faded into each other frame by frame,
  • the remainder of clip B after that zone.

The blend uses (1 - alpha) * A[i] + alpha * B[i], where alpha ramps from 0 to 1 across the transition. Total frame count comes out to len_a + len_b - transition, and the transition length is min(transition_frames, len_a, len_b) - so if your clips are shorter than the requested fade, it just fades across the whole overlap instead of crashing.

The inputs that matter

Only three, and none of them are scary:

  • transition_frames (int, default 10) - how many frames the dissolve spans. For 24–30 fps clips, 10–15 reads as a gentle dip; anything under ~6 starts to look like a hard cut with smearing.
  • mode - the easing curve on the fade: linear, ease_in, ease_out, ease_in_out, or sine. linear is fine for short fades; ease_in_out and sine are the ones that look "produced" for longer ones. ease_in starts slow, ease_out ends slow.
  • offset (int, default 0) - skip the first N frames of video B before starting the fade. Handy when clip B opens with noise or a blank lead-in you don't want dissolving over.

It returns images (the stitched IMAGE batch) and count (an INT - the new total frame count, which is genuinely useful for wiring downstream without recomputing). Wire images into whatever encodes: a save-animated-node, VHS_VideoCombine if you run VideoHelperSuite, or a frame-interpolator if you want to smooth the fade further.

Installing it

Standard, nothing exotic. Either search "ComfyUI-Rennart" in the Manager's Install Custom Nodes tab, or:

cd ComfyUI/custom_nodes
git clone https://github.com/Rennart2025/ComfyUI-Rennart

Then restart ComfyUI. There are no model downloads - the node only needs torch, and the pack's requirements.txt is a one-liner (torch\nnumpy) because everything else ComfyUI already ships. If the node doesn't appear under Rennart/Video, check the console for the [Rennart] loader warnings; the pack auto-imports every .py in its folder, so one broken file in the set can silence the others.

Where people get burned

The trap to internalize: you can't drop a video file path into this node. It takes image batches, so you must load your clips as frame sequences first - via a video-loader node (VideoHelperSuite or similar) or by feeding it the frames from an AnimateDiff-style generator. Beginners paste a file path in, get a type error, and assume the node is broken. It isn't; it's just not a video codec tool.

Second gotcha, straight from the source: if the two batches have different resolutions, the node silently resizes clip B down to clip A's size with bilinear interpolation and prints a warning to the console. Your output resolution follows clip A. For clean results, match resolutions upstream instead of letting it squash one side.

And a genuinely sharp edge: if offset is larger than clip B's frame count, create_crossfade bails and returns video_a untouched with count = 0 - no exception, no obvious error on the canvas. You'll just see your render "finish" with only clip A. If your output suddenly looks like clip B vanished, check offset against B's length.

For a dissolve that never leaves your workflow and needs no external encoder, it's hard to fault. Feed it matched-resolution frame batches, pick ease_in_out, and move on.

CategoryRennart/Video

Inputs (5)

NameTypeDefaultDescription
video_aIMAGE
video_bIMAGE
transition_framesINT101–500Количество кадров в зоне перехода
modeCOMBOlinearКривая изменения прозрачности
offsetINT00–1000Сдвиг начала перехода в кадрах (пропустить первые N кадров видео B)

Outputs (2)

NameTypeDescription
imagesIMAGE
countINT