Comparing encoder settings, showing two camera angles at once, reaction videos, a stabilisation before/after — there are plenty of reasons to put two or more videos in one frame. FFmpeg has three stacking filters for this: hstack (horizontal), vstack (vertical) and xstack (arbitrary layout). They are shorter to write and faster to run than working out coordinates for overlay.
The one thing everyone trips over the first time is the rule that inputs must have the same height (or width). This article starts there.
Verified: FFmpeg 8.1 on Windows — all 10 commands in this article were executed by the verification script and passed (2026-09-06).
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 untouched
Two 1920×1080 videos produce a 3840×1080 output. Resolutions are simply added together; 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 requires every input to have the same height; vstack requires the same width. Pixel formats must match as well. The fix is to scale 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 “height 720, width chosen to keep the aspect ratio and rounded to a multiple of 2”. Use -2 rather than -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
If the comparison should ship as 1080p, halve each input’s width 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 letterbox it to 1920×1080, append pad=1920:1080:0:(oh-ih)/2.
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 pixel count, so size and time roughly double. Shrinking first halves the pixel count relative to one original, and the result comes out smaller than a single clip. Pick the former when you need detail for a comparison and the latter when it’s going to 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 pile landscape clips into a 9:16 phone format. This time it’s the width that has to match.
Three or more inputs, grids (xstack)
Three in a row is just 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
A 2×2 grid uses xstack, listing each input’s top-left corner 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 / h0 stand for the width and height of input 0; sums like w0+w1 are allowed. Four 720p inputs give a 2560×1440 output.
With only three inputs the bottom-right cell is empty; set fill to choose its colour. Omitting it does not cause an error, but the contents of the empty area are undefined (not necessarily 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 only touch video, so you decide what happens to 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, so mixing two tracks lowers the overall level by about 6 dB. Disable that with amix=inputs=2:normalize=0 if it bothers you (watch for clipping).
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 make the boundary obvious, pad a few pixels onto the left input 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
Labels are drawn with drawtext on each input separately, then stacked.
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
Non-Latin labels need a font specified with fontfile=; see The drawtext filter.
FAQ
How is this different from overlay?
overlay places a foreground on top of a background — to put videos next to each other you first enlarge the canvas with pad and then compute where to place each one. hstack/vstack just line the inputs up, so the command is shorter and the processing lighter. For free positioning (picture-in-picture, for example) overlay is the right tool.
What if the frame rates differ?
There’s no error, but the output timestamps follow one input and the other can stutter. Match them first with fps=30 or similar.
I don’t want to read the same file twice
If you’re comparing the same footage with and without a filter, split one input into two branches 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