ComfyUI Node

Video Auto Sync HStack

Two Videos, Different FPS, Side by Side Without the Jank

By marcoc2·Created 2 years ago·Updated 5 months ago· 1
Video Auto Sync HStack
  • image1
  • image2
  • image
  • fps
fps18.00
fps248.00

Side-by-side comparisons are the backbone of honest testing - "here's the original, here's what my workflow did to it." But the moment your two clips have different frame rates, resolutions, or lengths, a naive horizontal concat becomes a stuttering, misaligned mess. VideoAutoSyncHStack is the fix: it resamples both videos to a common timebase, scales them to a common height, and stacks them into a single side-by-side clip. The output is the kind of thing you can actually post and have people believe.

It's part of AnotherUtils (marcoc2/ComfyUI-AnotherUtils), and it does its work with pure PyTorch - no ffmpeg subprocess, no re-encoding. The default inputs are a dead giveaway for the intended use: fps1 at 8 and fps2 at 48, which is exactly the classic LTX-video-draft-vs-final comparison (LTX is famous for churning out quick 8fps drafts). This node was built for that workflow.

How it works

The approach is "resample to the highest FPS, then keep only the shortest clip." Concretely:

  1. Target FPS = max(fps1, fps2), target height = the taller of the two inputs (padded to an even number, which video encoders are picky about).
  2. Both videos get resized (bilinear) to that height, keeping aspect ratio and making widths even.
  3. Each video's duration in seconds is computed (frames / fps), and the shorter duration wins.
  4. For every output frame at the target FPS, it picks the matching frame from each input by min(int(t * fps), last_frame) - duplicating or dropping frames the same way ffmpeg's fps filter does, but without re-encoding.

The result is a single clip at the target FPS whose length matches the shorter input. Frame alignment is "closest available frame," not interpolation, which is fine for comparison purposes and much faster.

The inputs

  • image1 / image2 - the two video frame batches (tensors of [B, H, W, C]). They don't need matching resolutions or lengths, but each should be internally uniform.
  • fps1 / fps2 - the actual frame rates of your inputs. Getting these wrong is the #1 way to produce garbage; the node trusts you, and if you say 8 when it's really 24, the sync drifts.

What comes out

  • image - the stacked side-by-side video, same length as the shorter input.
  • fps - the target frame rate, ready to feed a save-video node so the encoder uses the right number.

Installing it

Standard AnotherUtils install:

cd ComfyUI/custom_nodes
git clone https://github.com/marcoc2/ComfyUI-AnotherUtils.git

Restart ComfyUI, or search "AnotherUtils" in ComfyUI Manager. No models, no extra dependencies.

Where people get burned

The two classic mistakes are both about honesty of input. First, lying about the FPS - the node can't read your encoder's mind, so give it real numbers or the sync drifts further with every second. Second, expecting it to pad rather than trim: it cuts at the shortest duration, so if you wanted the longer clip to keep playing, this isn't the node for you. And a small thing: because it does nearest-frame resampling, fast motion in the lower-FPS clip can look slightly steppy. That's inherent to the approach, not a bug - if you need true motion-interpolated sync, look at a dedicated video-interpolation node instead.

CategoryAnotherUtils/Video

Inputs (4)

NameTypeDefaultDescription
image1IMAGE
fps1FLOAT8.000.1–120
image2IMAGE
fps2FLOAT48.000.1–120

Outputs (2)

NameTypeDescription
imageIMAGE
fpsFLOAT