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 inputhstack=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.
Related tool
The Side-by-side video tool stacks two videos in your browser without uploading them, and matches the heights automatically.
Related articles
- Resize a video (scale) — matching resolutions before stacking
- Fix “height not divisible by 2” — why
-2is used - Watermarks and overlays — compositing with
overlay - The drawtext filter — drawing label text