xfade joins two video streams with a transition — a crossfade, a wipe, a slide, a zoom — instead of a hard cut. It replaces the old approach of layering fade and overlay with time-shifted inputs, and it has more than forty built-in transitions. This article covers the two parameters that matter (offset and duration), the transition names, chaining more than two clips, matching audio with acrossfade, and the input requirements that account for most error messages.
Verified: FFmpeg 8.1 on Windows — all 17 commands in this article were executed by the verification script and passed (2026-09-06).
Basic syntax
Join two clips with a one-second crossfade:
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=4[v]" -map "[v]" output.mp4
| Parameter | Meaning | Default |
|---|---|---|
transition |
Transition type | fade |
duration |
Length of the transition in seconds | 1 |
offset |
When the transition starts, in seconds from the start of the first clip | 0 |
Working out offset
offset is measured from the start of the first clip. To crossfade over the last 2 seconds of a 10-second clip, use offset=8 with duration=2.
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:duration=2:offset=8[v]" -map "[v]" output.mp4
The output is offset + length of clip 2 long — the first 8 seconds of clip 1, then clip 2 in full, with its first 2 seconds blended over clip 1’s last 2. You don’t need -t to trim it.
The rule to remember: offset should be length of clip 1 − duration. If offset + duration is shorter than clip 1, the end of clip 1 is cut off (two 5-second clips with offset=1:duration=2 gave a 6-second output — 1 + 5). If offset is beyond the end of clip 1, the transition never happens: the output ends with clip 1 and clip 2 is silently dropped (the same clips with offset=8 produced a 5.1-second file).
Available transitions
Fades
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=4[v]" -map "[v]" output.mp4
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=fadeblack:duration=1:offset=4[v]" -map "[v]" output.mp4
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=fadewhite:duration=1:offset=4[v]" -map "[v]" output.mp4
fade blends directly from one clip to the other; fadeblack and fadewhite dip through black or white in the middle.
Slides
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=slideleft:duration=0.8:offset=4[v]" -map "[v]" output.mp4
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=slideright:duration=0.8:offset=4[v]" -map "[v]" output.mp4
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=slideup:duration=0.8:offset=4[v]" -map "[v]" output.mp4
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=slidedown:duration=0.8:offset=4[v]" -map "[v]" output.mp4
Wipes
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeleft:duration=0.5:offset=4[v]" -map "[v]" output.mp4
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipetl:duration=1:offset=4[v]" -map "[v]" output.mp4
Zoom and shape
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=zoomin:duration=0.8:offset=4[v]" -map "[v]" output.mp4
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1:offset=4[v]" -map "[v]" output.mp4
Full list of transition names
fade, fadeblack, fadewhite, distance, wipeleft, wiperight, wipeup, wipedown, slideleft, slideright, slideup, slidedown, smoothleft, smoothright, smoothup, smoothdown, circleopen, circleclose, vertopen, vertclose, horzopen, horzclose, dissolve, pixelize, diagtl, diagtr, diagbl, diagbr, hlslice, hrslice, vuslice, vdslice, hblur, fadegrays, wipetl, wipetr, wipebl, wipebr, squeezev, squeezeh, zoomin, fadefast, fadeslow
The names are literal: diagtl is a diagonal wipe from the top-left, hlslice slices horizontally from the left, squeezev squeezes vertically. For anything not on the list, transition=custom with an expr lets you write your own blend expression.
Crossfading the audio too (acrossfade)
xfade only handles video. To crossfade the audio over the same span, add acrossfade to the graph.
ffmpeg -i input.mp4 -i input.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=4[v];[0:a][1:a]acrossfade=d=1[a]" -map "[v]" -map "[a]" output.mp4
acrossfade=d=1 crossfades over one second. It has no offset — it always overlaps the end of the first stream with the start of the second — so video and audio line up only when offset = length of clip 1 − duration, which is the normal case. Without acrossfade, mapping -map 0:a -map 1:a would give two separate tracks, not a joined one.
Chaining three or more clips
Feed the result of the first xfade into the next:
ffmpeg -i input.mp4 -i input.mp4 -i input.mp4 \
-filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=4[v01];[v01][2:v]xfade=transition=fade:duration=1:offset=8[vout]" \
-map "[vout]" output.mp4
The second offset is measured from the start of the merged stream [v01], whose length is clip1 + clip2 − duration1. With three 5-second clips: [v01] is 5 + 5 − 1 = 9 s, so the second transition starts at 9 − 1 = 8. In general each new offset is previous offset + next clip length − duration. With many clips the arithmetic gets tedious, and a small script that reads durations with ffprobe and generates the filter string is the usual solution.
Input requirements
Both inputs of an xfade must have the same resolution, pixel format, frame rate and timebase, and both must be constant frame rate. Mismatches produce errors such as First input link main parameters (size 1920x1080 …) do not match the corresponding second input link or, for variable-frame-rate phone footage, a transition that arrives at the wrong time or a hang. Normalise first:
ffmpeg -i clip1.mp4 -vf "scale=1280:720,fps=30" -c:v libx264 clip1_norm.mp4
ffmpeg -i clip2.mp4 -vf "scale=1280:720,fps=30" -c:v libx264 clip2_norm.mp4
ffmpeg -i clip1_norm.mp4 -i clip2_norm.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=4[v]" -map "[v]" output.mp4
Or do it inside the graph: [0:v]scale=1280:720,fps=30,format=yuv420p,settb=AVTB[a];[1:v]scale=1280:720,fps=30,format=yuv420p,settb=AVTB[b];[a][b]xfade=....
FAQ
The output ends after the first clip and the second never shows
offset is beyond the end of the first clip, so the transition point is never reached. Check the clip’s duration with ffprobe and set offset = duration_of_clip1 − transition_duration.
Can xfade join files with a copy (no re-encode)?
No. Blending frames means decoding and re-encoding. If you don’t need a transition, the concat demuxer with -c copy is the fast option — see Join videos without re-encoding.
Which transition is cheapest to encode?
Any of them is negligible compared with the encode itself; the transition runs only over duration seconds. What costs time is re-encoding both full clips, which xfade requires regardless of the transition chosen.
Related articles
- Join videos without re-encoding — concat demuxer with
-c copy - Fade in, fade out and crossfade — single-clip fades and the older overlay method
- Audio fades —
afadeandacrossfadein detail - Trim a video — cutting clips to length before joining