How to Create Animated Split Screen with FFmpeg

This guide explains how to combine two video feeds into a split-screen composition using FFmpeg, where the dividing line between both inputs dynamically animates across the frame. By using FFmpeg's built-in blend filter with per-pixel mathematical expressions, you can smoothly move, sweep, or oscillate the split boundary over time without needing external video editing software.

Step 1: Normalize Video Dimensions and Framerates

The blend filter requires both video streams to have identical dimensions, framerates, and pixel formats. Pre-scale both inputs inside the filter graph to ensure compatibility:

ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex \
"[0:v]scale=1920:1080,fps=30,format=yuva420p[v0]; \
 [1:v]scale=1920:1080,fps=30,format=yuva420p[v1]" ...

Step 2: Create a Linear Moving Split

To create a boundary that moves linearly from the left edge to the right edge over a set duration (e.g., 10 seconds), use the blend filter's all_expr parameter.

ffmpeg -i left.mp4 -i right.mp4 -filter_complex \
"[0:v]scale=1920:1080,fps=30[v0]; \
 [1:v]scale=1920:1080,fps=30[v1]; \
 [v0][v1]blend=all_expr='if(lte(X, W*(t/10)), A, B)'[outv]" \
-map "[outv]" -map 0:a? -c:v libx264 -pix_fmt yuv420p output_linear.mp4

How the Expression Works:

  • X: The horizontal coordinate of the current pixel.
  • W: The total width of the video frame.
  • t: The current timestamp in seconds.
  • A: The pixel value from the first input ([v0]).
  • B: The pixel value from the second input ([v1]).
  • if(lte(X, W*(t/10)), A, B): If the current X-coordinate is less than or equal to the calculated boundary position, display input A; otherwise, display input B.

Step 3: Create an Oscillating (Back-and-Forth) Boundary

To make the split line smoothly swing back and forth across the center of the frame, introduce a trigonometric function (sin) into the expression:

ffmpeg -i left.mp4 -i right.mp4 -filter_complex \
"[0:v]scale=1920:1080,fps=30[v0]; \
 [1:v]scale=1920:1080,fps=30[v1]; \
 [v0][v1]blend=all_expr='if(lte(X, (W/2) + (W/4)*sin(2*PI*t/4)), A, B)'[outv]" \
-map "[outv]" -map 0:a? -c:v libx264 -pix_fmt yuv420p output_oscillating.mp4
  • (W/2) sets the center anchor point of the split.
  • (W/4)*sin(...) limits the travel distance to 25% of the frame width to the left and right.
  • 2*PI*t/4 controls the oscillation speed, completing one full cycle every 4 seconds.

Step 4: Add a Colored Dividing Line

To clearly separate the two videos, you can highlight the animated boundary with a solid border line. This condition checks if the pixel falls within a narrow coordinate window around the boundary and overrides the color value:

ffmpeg -i left.mp4 -i right.mp4 -filter_complex \
"[0:v]scale=1920:1080,fps=30[v0]; \
 [1:v]scale=1920:1080,fps=30[v1]; \
 [v0][v1]blend=all_expr='if(between(X, (W/2 + (W/4)*sin(2*PI*t/4)) - 3, (W/2 + (W/4)*sin(2*PI*t/4)) + 3), 255, if(lte(X, (W/2 + (W/4)*sin(2*PI*t/4))), A, B))'[outv]" \
-map "[outv]" -map 0:a? -c:v libx264 -pix_fmt yuv420p output_border.mp4
  • between(X, Boundary - 3, Boundary + 3) creates a 6-pixel wide strip where the boundary currently sits.
  • 255 forces the pixels inside that strip to maximum luminance, creating a visible white divider.

Step 5: Audio Handling

In the commands above:

  • -map "[outv]" selects the processed video stream.
  • -map 0:a? includes the audio from the first video if present.
  • To mix the audio tracks of both source files simultaneously, add the amix filter:
ffmpeg -i left.mp4 -i right.mp4 -filter_complex \
"[0:v]scale=1920:1080,fps=30[v0]; \
 [1:v]scale=1920:1080,fps=30[v1]; \
 [v0][v1]blend=all_expr='if(lte(X, W*(t/10)), A, B)'[outv]; \
 [0:a][1:a]amix=inputs=2:duration=first[outa]" \
-map "[outv]" -map "[outa]" -c:v libx264 -pix_fmt yuv420p output_mixed_audio.mp4