To put two or more videos in one frame, FFmpeg has three stacking filters: hstack (side by side), vstack (one above the other) and xstack (any layout). They are shorter to write and faster to run than working out coordinates for overlay. Common uses are comparing encoder settings, a stabilisation before/after, two camera angles at once, and reaction videos.

Most first attempts fail on one rule: the inputs must have the same height (or width).

Horizontal (hstack) basics

ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v][1:v]hstack=inputs=2[v]" -map "[v]" -map 0:a -c:a copy output.mp4
  • [0:v][1:v] — the video streams of the first and second input
  • hstack=inputs=2 — place the two side by side, left to right
  • -map "[v]" — use the stacked result as the output video
  • -map 0:a -c:a copy — keep the left video’s audio as it is

Two 1920×1080 videos give a 3840×1080 output. The widths are added together, and nothing is scaled down for you.

The height-mismatch error

[Parsed_hstack_0] Input 1 height 720 does not match input 0 height 1080.
[Parsed_hstack_0] Failed to configure output pad on Parsed_hstack_0
Error reinitializing filters!

hstack needs every input to have the same height, and vstack needs the same width. Different pixel formats are fine, because FFmpeg converts them automatically. To fix the error, scale the inputs first.

ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v]scale=-2:720[l];[1:v]scale=-2:720[r];[l][r]hstack=inputs=2[v]" -map "[v]" -map 0:a output.mp4

scale=-2:720 means “make the height 720, and pick the width that keeps the aspect ratio, rounded to a multiple of 2”. Use -2, not -1, because an odd width makes libx264 stop with width not divisible by 2 (see Fix “height not divisible by 2”).

Keep the output at the original resolution

To keep the comparison at 1080p, halve the width of each input before stacking.

ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v]scale=960:-2[l];[1:v]scale=960:-2[r];[l][r]hstack=inputs=2[v]" -map "[v]" -map 0:a output.mp4

A 16:9 source scaled to 960 wide becomes 960×540, so the output is 1920×540. To make it 1920×1080 with black bars above and below, add pad=1920:1080:0:(oh-ih)/2 at the end of the filter.

Measured: output size and encode time

Two copies of the same 1920×1080, 30 fps, 10-second clip, stacked and encoded with libx264 -preset veryfast -crf 23 (i9-14900KF, FFmpeg 8.1, measured 2026-09-02).

Arrangement Output resolution File size Time
One clip re-encoded (baseline) 1920×1080 5.1 MB 1.1 s
Stacked as-is 3840×1080 10.2 MB 2.1 s
Each scaled to 960 wide, then stacked 1920×540 3.0 MB 1.2 s

Stacking at full size doubles the number of pixels, so the file size and the time roughly double. Shrinking first leaves half the pixels of one original, so the result is smaller than a single clip. Stack at full size when you need to compare fine detail, and shrink first for social media.

Vertical (vstack)

ffmpeg -i top.mp4 -i bottom.mp4 -filter_complex "[0:v][1:v]vstack=inputs=2[v]" -map "[v]" -map 0:a output.mp4

Two 1080p clips stacked give 1920×2160. Use hstack to put two portrait (1080×1920) videos next to each other, and vstack to put landscape clips one above the other. Two 16:9 clips stacked are only 8:9, so for a 9:16 phone format you also need scale and pad to shrink them and add bars above and below. With vstack, the width must match.

Three or more inputs, grids (xstack)

For three in a row, use hstack=inputs=3.

ffmpeg -i a.mp4 -i b.mp4 -i c.mp4 -filter_complex "[0:v][1:v][2:v]hstack=inputs=3[v]" -map "[v]" output.mp4

For a 2×2 grid, use xstack and list the top-left corner of each input in layout.

ffmpeg -i a.mp4 -i b.mp4 -i c.mp4 -i d.mp4 -filter_complex "[0:v][1:v][2:v][3:v]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[v]" -map "[v]" output.mp4

How to read layout:

Entry Meaning
0_0 input 0 at top-left (x=0, y=0)
w0_0 input 1 at x = width of input 0, y = 0 (top-right)
0_h0 input 2 at x = 0, y = height of input 0 (bottom-left)
w0_h0 input 3 at bottom-right

w0 and h0 are the width and height of input 0. You can also add them up, as in w0+w1. Four 720p inputs give a 2560×1440 output.

With only three inputs, the bottom-right cell is empty. Set fill to choose its colour. Leaving it out does not cause an error, but the empty area can contain anything (it is not always black).

ffmpeg -i a.mp4 -i b.mp4 -i c.mp4 -filter_complex "[0:v][1:v][2:v]xstack=inputs=3:layout=0_0|w0_0|0_h0:fill=black[v]" -map "[v]" output.mp4

Audio

The stack filters handle only video, so you choose what to do with the audio.

Use one input’s audio (the common case) — -map 0:a or -map 1:a.

Mix both — add amix to the same filter graph.

ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v][1:v]hstack=inputs=2[v];[0:a][1:a]amix=inputs=2[a]" -map "[v]" -map "[a]" output.mp4

By default amix scales each input by 1/N (N is the number of inputs), so with two tracks each one is about 6 dB quieter in the mix than on its own. If that bothers you, turn it off with amix=inputs=2:normalize=0, but watch for clipping (distortion when the sum gets too loud).

No audio — -an.

Inputs of different length

By default, the output runs as long as the longest input, and the shorter one freezes on its last frame. To stop when the shorter one ends, add shortest=1.

ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v][1:v]hstack=inputs=2:shortest=1[v]" -map "[v]" output.mp4

Divider line and Before / After labels

To show a clear line between the two videos, add a few pixels to the left input with pad before stacking.

ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v]pad=iw+10:ih:0:0:color=black[l];[l][1:v]hstack=inputs=2[v]" -map "[v]" output.mp4

Draw each label with drawtext on its own input, then stack the inputs.

ffmpeg -i left.mp4 -i right.mp4 -filter_complex "[0:v]drawtext=text='Before':fontsize=24:fontcolor=white:x=10:y=10[l];[1:v]drawtext=text='After':fontsize=24:fontcolor=white:x=10:y=10[r];[l][r]hstack=inputs=2[v]" -map "[v]" output.mp4

Labels in non-Latin scripts need a font set with fontfile=. See The drawtext filter.

FAQ

How is this different from overlay?

overlay puts one video on top of another. To place videos side by side with it, you first make the canvas bigger with pad, then work out where each video goes. hstack/vstack line the inputs up for you, so the command is shorter and FFmpeg has less work to do. For free placement, such as picture-in-picture, use overlay.

What if the frame rates differ?

There is no error, but the output gets a new frame each time either input has one, so the frames come at uneven intervals and the motion can look jerky. Make them match first with fps=30 or similar.

I don’t want to read the same file twice

To compare the same footage with and without a filter, split one input into two copies and filter only one: [0:v]split[a][b];[b]eq=contrast=1.3[c];[a][c]hstack.

The Side-by-side video tool stacks two videos in your browser without uploading them, and matches the heights automatically.