This project is a port of standard easing equations, CSS easings and many ported GLSL transitions for use in tandem with easing or alone. The easing expressions can be used for other filters besides xfade.

There are 2 variants: 1. custom ffmpeg build with
added xfade easing and reverse options 2.
custom expressions for use with standard ffmpeg (see performance)
Xfade is a FFmpeg video transition filter with many built-in transitions and an expression evaluator for custom transitions. However the progress rate is linear, starting and stopping abruptly and proceeding at constant speed, therefore transitions lack interest. Easing inserts a progress envelope to smooth transitions in a natural way.
Example usage: * custom ffmpeg: set the new
easing option to the easing name, with optional CSS-easing
arguments, and the transition option to the transition
name, with optional transition arguments, and the reverse
option to reverse the easing and/or transition effect (see reversing).
Example (quartic-out, radial):
xfade=duration=3:offset=10:easing=quartic-out:transition=radial
Example (CSS-easing, ported-GLSL transition, reversed):
xfade=duration=3:offset=10:easing='cubic-bezier(0.12,0.57,0.63,0.21)'
:transition='gl_cube(floating=5,unzoom=0.8,background=SlateGray)':reverse=1
transition option to custom and the
expr option to the concatenation of a standard easing
expression and a transition expression (this variant does not support
CSS easings or reversed effects).xfade=duration=3:offset=10:transition=custom:expr='st(0,P^4);st(1,atan2(X-W/2,Y-H/2)-(ld(0)-0.5)*PI*2.5); st(1,st(1,clip(ld(1),0,1))*ld(1)*(3-2*ld(1))); B*ld(1)+A*(1-ld(1))'A CLI wrapper script is provided to generate custom expressions, test videos, slideshows and more. It also facilitates generic easing of ffmpeg filters – see Easing other filters.
The custom ffmpeg variant has backward compatible xfade arguments, is fast with a simple C API and no restrictions. Installation involves a few patches to a single ffmpeg C source file, with no dependencies. The custom expression variant is convenient but clunky – see performance – and runs on plain vanilla ffmpeg but with restrictions: it doesn’t support CSS easings, certain transitions, the reverse feature, full colour or textures.
ffmpeg -i first.mp4 -i second.mp4 -filter_complex "
xfade=duration=3:offset=1:easing=cubic-in-out:transition=wipedown
" output.mp4
Easing mode in-out is the default mode; the above is
equivalent to easing=cubic.
The default easing is linear (none).
ffmpeg -i first.mp4 -i second.mp4 -filter_complex_threads 1 -filter_complex "
xfade=duration=3:offset=1:transition=custom:expr='
st(0, if(lt(P, 0.5), 4 * P^3, 1 - 4 * (1-P)^3)) ;
if(gt(Y, H*(1-ld(0))), A, B)
'" output.mp4
Here, the expr parameter is shown on two lines for
clarity.
The first line is the easing expression e(P)
(cubic in-out) which stores its calculated progress value
in st(0).
The second line is the transition expression t(e(P))
(wipedown) which loads its eased progress value from
ld(0) instead of P. The semicolon token
combines expressions.
[!NOTE] ffmpeg option
-filter_complex_threads 1is required because ffmpeg expression state variables (thest()&ld()functions) are shared between slice processing jobs and therefore not thread-safe, consequently processing is slower
In this example you can copy the easing expression from file xfade-easings-inline.txt and the transition expression from eased-transitions-rgb24-inline.txt or eased-transitions-yuv420p-inline.txt. Those contain inline expressions for CLI use.
Alternatively use the CLI script:
xfade-easing.sh -t wipedown -e cubic -x -
dumps the xfade expr parameter:
'st(0,if(lt(P,0.5),4*P^3,1-4*(1-P)^3));if(gt(Y,H*(1-ld(0))),A,B)'
Some expressions are very long, so using a filtergraph script keeps things manageable and readable.
For this same example you can copy the easing expression from file xfade-easings-script.txt and the transition expression from eased-transitions-rgb24-script.txt or eased-transitions-yuv420p-script.txt. Those contain multiline expressions for script use (but the inline expressions work too).
Alternatively use xfade-easing.sh with
expansion specifiers expr='%n%X' (see Usage):
xfade-easing.sh -t wipedown -e cubic -s "xfade=offset=10:duration=5:transition=custom:expr='%n%X'" -x script.txt
writes the complete xfade filter description to file script.txt:
xfade=offset=10:duration=5:transition=custom:expr='
st(0, if(lt(P, 0.5), 4 * P^3, 1 - 4 * (1-P)^3))
;
if(gt(Y, H * (1 - ld(0))), A, B)'
and the command becomes
ffmpeg -i first.mp4 -i second.mp4 -filter_complex_threads 1 -/filter_complex script.txt output.mp4`
[!NOTE] this option syntax has changed but is undocumented: - for ffmpeg version 7+ use
-/filter_complex filename- for earlier versions use-filter_complex_script filename
For simplicity, native xfade-easing is a header-only implementation in xfade-easing.h patched into vf_xfade.c at an optimal place. It comprises static functions only, sharing internal linkage with the vf_xfade.c compilation unit, so no Makefile changes are necessary.
tar -xJf ffmpeg-x.x.x.tar.xz or use
xz/gunzip/etc.cd ffmpeg and patch libavfilter/vf_xfade.c:
patch -b -u -N -p0 -i vf_xfade.patch (saves backup
as vf_xfade.c.orig)vf_xfade.patchlibavfilter/PATH,LD_LIBRARY_PATH,LDFLAGS
environment variables to find the package components./configure with any --prefix and
other options (drawtext requires --enable-libfreetype
--enable-libharfbuzz
--enable-libfontconfig)ffmpeg -hide_banner -buildconf and copy-paste the options
(I maintain a conf file and source that)./configure will flag up any missing library packagesmake ECFLAGS=-Wno-declaration-after-statement, it
takes a whileECFLAGS settingld: warning: text-based stub file are out of sync warnings
is heremake install or point PATH
to the ffmpeg source rootffmpeg -hide_banner --help filter=xfade: the
xfade AVOptions should include easing and
reverseMy repo ffmpeg-makexe has a Bash script to build ffmpeg easily with (or without) xfade-easing under MSYS2 in two dispositions:
Native Msys2 Windows build using media-autobuild_suite is also possible but it broke for me. Both that and ffmpeg-windows-build-helpers are complex scripts promoted by the FFmpeg team.
I have not explored WSL or Cygwin.
Cross compiling using ffmpeg-windows-build-helpers
on Linux is the method I have had most success with. It built a static
feature-rich ffmpeg.exe with xfade-easing on a VirtualBox Ubuntu client running
on Macos, but attempting the same process natively on Macos failed –
needs investigation. You need to follow steps 2-4 above first and use
the --ffmpeg-source-dir option.
Please see the FFmpeg Compilation Guide.
FFmpeg contains x264 and other components which require compliance with the GPL, therefore I am unable to distribute binary executables of ffmpeg with xfade-easing.
The custom FFmpeg version has been built and tested on Macos with
clang, Ubuntu Linux and Msys2 with gcc and
clang, and Windows with MSVC and
clang-cl.
Pre-generated easing and transition expressions are in the expr/ subdirectory for mix and match use. The CLI script can produce combined expressions in
any syntax using expansion specifiers (like printf).
This format is condensed into a single line stripped of whitespace.
Example: elastic out easing (leaves progress in
st(0))
st(0,cos((1-P)*20.944)/2^(10*(1-P)))
This format is best for expressions that are too unwieldy for inline ffmpeg commands.
Example: gl_Swirl transition (expects eased
progress in ld(0)) (cf. Swirl.glsl)
st(1, 1);
st(2, 1);
st(3, X / W - 0.5);
st(4, 0.5 - Y / H);
st(5, hypot(ld(3), ld(4)));
if(lt(ld(5), ld(1)),
st(1, (ld(1) - ld(5)) / ld(1));
st(5, 1 - 2 * abs(ld(0) - 0.5));
st(1, ld(1) * ld(1) * ld(5) * 8 * PI);
ifnot(ld(2), st(1, -ld(1)));
st(5, sin(ld(1)));
st(6, cos(ld(1)));
st(1, ld(3) * ld(6) - ld(4) * ld(5));
st(4, ld(3) * ld(5) + ld(4) * ld(6));
st(3, ld(1))
);
st(3, (ld(3) + 0.5) * W);
st(4, (0.5 - ld(4)) * H);
st(5, ifnot(PLANE, a0(ld(3),ld(4)), ifnot(1-PLANE, a1(ld(3),ld(4)), ifnot(2-PLANE, a2(ld(3),ld(4)), a3(ld(3),ld(4))))));
st(6, ifnot(PLANE, b0(ld(3),ld(4)), ifnot(1-PLANE, b1(ld(3),ld(4)), ifnot(2-PLANE, b2(ld(3),ld(4)), b3(ld(3),ld(4))))));
ld(6) * (1 - ld(0)) + ld(5) * ld(0)
These use P directly for progress instead of
ld(0).
Example: gl_WaterDrop transition (cf. WaterDrop.glsl)
st(1, 30);
st(2, 30);
st(3, 1 - P);
st(4, X / W - 0.5);
st(5, 0.5 - Y / H);
st(6, hypot(ld(4), ld(5)));
st(7, if(lte(ld(6), ld(3)),
st(1, sin(ld(6) * ld(1) - ld(3) * ld(2)));
st(4, ld(4) * ld(1));
st(5, ld(5) * ld(1));
st(4, X + ld(4) * W);
st(5, Y - ld(5) * H);
ifnot(PLANE, a0(ld(4),ld(5)), ifnot(1-PLANE, a1(ld(4),ld(5)), ifnot(2-PLANE, a2(ld(4),ld(5)), a3(ld(4),ld(5))))),
A
));
ld(7) * (1 - ld(3)) + B * ld(3)
These ease ld(0) instead of P - see Easing other filters.
This implementation derives from Michael Pohoreski’s single argument version of Robert Penner’s easing functions, further optimised by me for the peculiarities of xfade.
quadraticcubicquarticquinticsinusoidalexponentialcircularelasticbackbounce
squarerootcuberootThe squareroot and cuberoot easings focus
more on the middle regions and less on the extremes, opposite to
quadratic and cubic respectively:
Here are all the above easings superimposed using the Desmos Graphing Calculator:
The custom ffmpeg variant supports CSS Easing Functions Level 2 which are too complex for custom expressions:
linear linear()ease ease-in ease-out
ease-in-out cubic-bezier()step-start step-end
steps()The new CSS linear() function can approximate any
progress contour by interpolating between adjacent points, documented at
W3C
here. There’s a CSS Linear()
Generator online by its pioneer Jake Archibald to convert easings
expressed in JavaScript or SVG to linear().
There are 4 fixed CSS smoothing curves and a general
cubic-bezier() easing function documented at W3C
here. See also the CSS Cubic
Bezier Generator to craft your own. The implementation used here is
transpiled from Apple’s open-source Webkit.
The CSS steps() staircase function is for transitions
that jump a constant amount, documented at W3C
here.
Standard easings have ease-in and ease-out modes but not CSS easings. See Reversing xfade effects to achieve this with the custom ffmpeg build.
The elastic and back easings overshoot and
undershoot, causing many transitions to clip and others to show colour
distortion. Therefore they are quite useless for xfade (but see Easing other filters). CSS easings
linear() and cubic-bezier() can also
overshoot.
Rendering expressions can only access the two frames of data available. A wrapping overshoot strategy might work for simple horizontal/vertical effects whereby fetching X & Y pixel data is intercepted but at present eased progress outside the range 0 to 1 yields unpredictable results.
The easing expressions are useful for filters other than xfade, e.g. blend, drawtext, geq, overlay, rotate, zoompan, etc. – anywhere an ffmpeg expr is used to calculate filter options.
For this purpose the CLI script includes
text expansion codes %g & %G to generate
generic easing expressions for the value in ld(0) (instead
of P for xfade), leaving the result in ld(0).
You can also copy generic easing expressions from file generic-easings-inline.txt
for inline -filter_complex use, or generic-easings-script.txt
for -/filter_complex scripts.
To ease other filters, store a normalised input value in
st(0,…), append the easing expression, then scale the eased
result left in ld(0).
Example: zoompan filter with elastic-out
zooming
Here’s the zoom option expression for the zoompan
filter:
zoom='st(0, clip((time - 1) / 3, 0, 1));
st(0, 1 - cos(ld(0) * 20.944) / 2^(10 * ld(0)));
lerp(1, 3, ld(0))'
The first line stores a 3 second duration delayed by 1 second
normalised to a value between 0 and 1.
The last line scales the result to zoom between 1x and 3x.
The middle line performs elastic-out easing, obtained from
generic-easings-script.txt,
or
xfade-easing.sh -e elastic-out -s %G -x -
The zoompan
filter can produce impressive Ken Burns
effects when zoom, x, y are
all dynamic. Adding easing can take the illusion of motion even
further.
Example: zoompan with back zooming and drawtext
with squareroot scrolling
The initial zoom here is 1.2x to accommodate the 10% undershoot that
back easing produces. So the zoompan zoom
expression, with back expr from generic-easings-inline.txt,
is:
z='st(0, clip((time - 1) / 3, 0, 1));
st(0,if(lt(ld(0),0.5),2*ld(0)*ld(0)*(2*ld(0)*3.59491-2.59491),1-2*(1-ld(0))^2*(4.59491-2*ld(0)*3.59491)));
lerp(1.2, 3.1, ld(0))'
And the drawtext y expression with
squareroot easing is:
y='st(0, clip((t - 1) / 3, 0, 1));
st(0, if(lt(ld(0), 0.5), sqrt(ld(0) / 2), 1 - sqrt((1-ld(0)) / 2)));
lerp(line_h - 10, h - line_h * 2 - 5, ld(0))'
For the custom expression variant, xfade transitions are provided as
custom expressions for use with easing, converted from C-code in vf_xfade.c.
The custom ffmpeg variant eases the built-in xfade transitions. Omitted
transitions are distance and hblur which
perform aggregation, so cannot be processed efficiently on a per
plane-pixel basis.
fade fadefast fadeslowfadeblack fadewhite
fadegrayswipeleft wiperight wipeup
wipedownwipetl wipetr wipebl
wipebrslideleft slideright slideup
slidedownsmoothleft smoothright
smoothup smoothdowncirclecrop rectcropcircleopen circleclosevertopen vertclose horzopen
horzclosediagtl diagtr diagbl
diagbrhlslice hrslice vuslice
vdsliceradial zoomindissolve pixelizesqueezeh squeezevhlwind hrwind vuwind
vdwindcoverleft coverright coverup
coverdownrevealleft revealright
revealup revealdownHere are the xfade transitions processed using custom expressions instead of the built-in transitions (for testing), without easing – see also the FFmpeg Wiki Xfade page:
The open collection of GL Transitions initiative lead by Gaëtan Renaudeau (gre) “aims to establish an universal collection of transitions that various softwares can use” released under a Free License.
Other GLSL transition sources are from shadertoy and the Vegas Forum.
Most of the transitions at gl-transitions and many from elsewhere have been transpiled into native C transitions (for custom ffmpeg variant) and custom expressions (for custom expression variant) for use with or without easing.
All GLSL transitions adapted to the GL Transition Specification are in glsl/.
The following list shows the transition names, customisation parameters and defaults, and authors:
| transition | parameters (=default) | author |
|---|---|---|
| gl_angular | startingAngle=90clockwise=0 |
Fernando Kuteken |
| gl_Bars | vertical=0 |
Mark Craig |
| gl_blend※ | mode=0 |
scriptituk |
| gl_BookFlip | hong | |
| gl_Bounce | bounces=3direction=0shadowAlpha=0.6shadowHeight=0.075shadowColor=0 |
Adrian Purser |
| gl_BowTie※ | vertical=0 |
huynx |
| gl_ButterflyWaveScrawler※ | amplitude=1waves=30colorSeparation=0.3 |
mandubian |
| gl_cannabisleaf | Flexi23 | |
| gl_chessboard | grid=8 |
lql |
| gl_CornerVanish | Mark Craig | |
| gl_CrazyParametricFun | a=4b=1amplitude=120smoothness=0.1 |
mandubian |
| gl_crosshatch | center.x=0.5center.y=0.5threshold=3fadeEdge=0.1 |
pthrasher |
| gl_CrossOut | smoothness=0.05 |
Mark Craig |
| gl_crosswarp | Eke Péter | |
| gl_CrossZoom※ | strength=0.4centerFrom.x=0.25centerFrom.y=0.5centerTo.x=0.75centerTo.y=0.5 |
rectalogic |
| gl_cube | persp=0.7unzoom=0.3reflection=0.4floating=3background=0 |
gre |
| gl_Diamond | smoothness=0.05 |
Mark Craig |
| gl_DirectionalScaled | direction.x=0direction.y=1scale=0.7background=0 |
Thibaut Foussard |
| gl_directionalwarp | smoothness=0.1direction.x=-1direction.y=1 |
pschroen |
| gl_doorway | reflection=0.4perspective=0.4depth=3background=0 |
gre |
| gl_DoubleDiamond | smoothness=0.05 |
Mark Craig |
| gl_Dreamy | mikolalysenko | |
| gl_EdgeTransition※ | edgeThickness=0.001edgeBrightness=8 |
Woohyun Kim |
| gl_Exponential_Swish※ | zoom=0.8angle=0offset.x=0offset.y=0exponent=4wrap.x=2wrap.y=2blur=0background=0 |
Boundless |
| gl_fadecolor※ | color=0colorPhase=0.4 |
gre |
| gl_FanIn | smoothness=0.05 |
Mark Craig |
| gl_FanOut | smoothness=0.05 |
Mark Craig |
| gl_FanUp | smoothness=0.05 |
Mark Craig |
| gl_Flower | smoothness=0.05rotation=360 |
Mark Craig |
| gl_GridFlip※ | size.x=4size.y=4pause=0.1dividerWidth=0.05randomness=0.1background=0 |
TimDonselaar |
| gl_heart | gre | |
| gl_hexagonalize | steps=50horizontalHexagons=20 |
Fernando Kuteken |
| gl_InvertedPageCurl | angle=100radius=0.159reverseEffect=0 |
Hewlett-Packard |
| gl_kaleidoscope | speed=1angle=1power=1.5 |
nwoeanhinnogaehr |
| gl_LinearBlur | intensity=0.1 |
gre |
| gl_Lissajous_Tiles※ | grid.x=10grid.y=10speed=0.5freq.x=2freq.y=3offset=2zoom=0.8fade=3power=3background=0 |
Boundless |
| gl_morph※ | strength=0.1 |
paniq |
| gl_Mosaic | endx=2endy=-1 |
Xaychru |
| gl_perlin | scale=4smoothness=0.01 |
Rich Harris |
| gl_pinwheel | speed=2 |
Mr Speaker |
| gl_polar_function | segments=5 |
Fernando Kuteken |
| gl_PolkaDotsCurtain | dots=20centre.x=0centre.y=0 |
bobylito |
| gl_powerKaleido | scale=2z=1.5speed=5 |
Boundless |
| gl_randomNoisex | towrabbit | |
| gl_randomsquares | size.x=10size.y=10smoothness=0.5 |
gre |
| gl_ripple | amplitude=100speed=50 |
gre |
| gl_Rolls | type=0rotDown=0 |
Mark Craig |
| gl_RotateScaleVanish | fadeInSecond=1reverseEffect=0reverseRotation=0background=0trkMat=0 |
Mark Craig |
| gl_rotateTransition | haiyoucuv | |
| gl_rotate_scale_fade | centre.x=0.5centre.y=0.5rotations=1scale=8background=0.15 |
Fernando Kuteken |
| gl_SimpleBookCurl※ | angle=150radius=0.1shadow=0.2 |
scriptituk |
| gl_SimplePageCurl | angle=80radius=0.15roll=0reverseEffect=0greyBack=0opacity=0.8shadow=0.2 |
Andrew Hung |
| gl_Slides | type=0slideIn=0 |
Mark Craig |
| gl_squareswire | squares.x=10squares.y=10direction.x=1.0direction.y=-0.5smoothness=1.6 |
gre |
| gl_StageCurtains※ | color=0xCC1A33FFbumps=15drop=0.1 |
scriptituk |
| gl_StarWipe | borderThickness=0.01starRotation=0.75borderColor=1 |
Ben Lucas |
| gl_static_wipe | upToDown=1maxSpan=0.5 |
Ben Lucas |
| gl_StereoViewer※ | zoom=0.9radius=0.25flip=0background=0trkMat=0 |
Ted Schundler |
| gl_Stripe_Wipe※ | nlayers=3layerSpread=0.5color1=0x3319CCFFcolor2=0x66CCFFFFshadowIntensity=0.7shadowSpread=0angle=0 |
Boundless |
| gl_swap | reflection=0.4perspective=0.2depth=3background=0 |
gre |
| gl_Swirl | radius=1clockwise=1 |
Sergey Kosarevsky |
| gl_WaterDrop | amplitude=30speed=30 |
Paweł Płóciennik |
| gl_windowblinds | Fabien Benetou | |
| gl_windowslice | count=10smoothness=0.5 |
gre |
※ native build only
Here are the ported GLSL transitions with default parameters and no easing. They are all supported by the custom ffmpeg variant but check above for the dozen or so that are not supported by the custom expression variant.
See also the GL Transitions Gallery which lacks many recent contributor transitions plus even more stacking up as pull requests – which is why I have not added my bundle.
GLSL transitions can also be eased, although easing is integral with some:
Example: Swirl transition with
bounce easing
Many GLSL transitions accept parameters to customise the transition effect. The parameters and default values are shown above.
Example: two pinwheel speeds:
'gl_pinwheel(0.5)' and 'gl_pinwheel(10)'
Parameters are appended to the transition name as CSVs within parenthesis.
For the custom ffmpeg variant the parameters may be name=value pairs
in any order, e.g. gl_WaterDrop(speed=20,amplitude=50), or
they may be indexed values, as follows.
For the custom expression variant the parameters must be indexed
values only but empty values assume defaults,
e.g. gl_GridFlip(5,3,,0.1,,1) arguments are
size.x=5,size.y=3,dividerWidth=0.1,background=1
with default values for other parameters.
Custom expressions can also be amended directly:
parameters are specified using store functions st(p,v)
where p is the parameter number and v its
value. So for gl_pinwheel with a speed value
10, change the first line of its expr below to
st(1, 10);.
st(1, 2);
st(2, 1 - ld(0));
st(1, atan2(0.5 - Y / H, X / W - 0.5) + ld(2) * ld(1));
st(1, mod(ld(1), PI / 4));
if(lte(ld(2), ld(1)), A, B)
Similarly, gl_directionalwarp takes 3 parameters:
smoothness, direction.x,
direction.y (from xfade-easing.sh -L) and its
expr starts with 3 corresponding st() (store) functions
which may be changed from their default values:
st(1, 0.1);
st(2, -1);
st(3, 1);
st(4, hypot(ld(2), ld(3)));
etc.
gl_angular has an additional clockwise
parametergl_Bounce has an additional direction
parameter to control bounce direction: 0=south,
1=west, 2=north, 3=east; also a
shadowColor parametergl_BowTie combines BowTieHorizontal and
BowTieVertical using parameter verticalgl_CrossZoom has additional
centerFrom,centerTo parameters to tune the
cross traversalgl_Exponential_Swish option blur default
was originally 0.5 but blurring makes it unacceptably
slowgl_InvertedPageCurl omits anti-aliased edges and takes
3 parameters:
angle may be 100 (default) or
30 degrees from horizontal eastradius is the cylinder radiusreverseEffect produces an uncurl effect (custom ffmpeg
only)gl_RotateScaleVanish has an additional
trkMat parameter (track matte, custom ffmpeg only) which
treats the moving image/video as a variable-transparency overlay – see
Dr Who example under Transparencygl_StereoViewer (custom ffmpeg only) has an additional
flip parameter to flip the split angles, a
background parameter and a trkMat
parametergl_Swirl has an additional clockwise
parameter to change directionbackground parameter for
all GL Transitions that show a black background during their transition,
e.g. gl_cube and gl_doorway, see Colour parameters and Backgrounds.Example: gl_InvertedPageCurl 30° with uncurl
(useful for sheet music with repeats)
'gl_InvertedPageCurl(30,0.15,0)' and
'gl_InvertedPageCurl(30,0.15,1)' concatenated
GLSL shader code runs on the GPU in real time, unlike ffmpeg. However GL Transition and Xfade APIs are broadly similar and non-complex algorithms are easily ported using simple vector resolution.
| context | GL Transitions | Xfade filter | notes |
|---|---|---|---|
| progress | uniform float progress
moves from 0 to 1 |
P moves from 1 to 0 |
progress ≡ 1 - P |
| ratio | uniform float ratio |
W / H |
|
| coordinates | vec2 uv uv.y == 0 is bottom uv == vec2(1.0) is
top-right |
X, Y Y == 0 is top (X,Y) == (W,H) is
bottom-right |
GL width and height are normalised uv.x ≡ X / W
uv.y ≡ 1 - Y / H |
| texture | vec4 getFromColor(vec2 uv)
|
a0(x,y) to
a3(x,y) or A for first input |
GL colour values are normalised GL function runs for every pixel xfade expr runs for every component (plane) of
every pixel |
| plane data | normalised RGBA | GBRA or YUVA unsigned integer | xfade bit depth depends on pixel format |
| precision | single | double | (float type) |
The custom ffmpeg variant, like GL Transitions, operates on single precision unit interval coordinate and colour data, processing all planes together. SIMD (Single Instruction Multiple Data) processing is compiler-dependent but heavy use is made of inline code and optimisation hints.
To make the transpiled code easier to follow, original variable names from the GLSL and xfade source code are retained in xfade-easing.sh and xfade-easing.h. The CLI script uses pseudo functions to emulate real functions, expanding them inline later.
Example: porting transition
gl_randomsquares
uniform ivec2 size; // = ivec2(10, 10)
uniform float smoothness; // = 0.5
float rand (vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
vec4 transition(vec2 p) {
float r = rand(floor(vec2(size) * p));
float m = smoothstep(0.0, -smoothness, r - (progress * (1.0 + smoothness)));
return mix(getFromColor(p), getToColor(p), m);
}xfade-easing.sh (custom expression variant):
gl_randomsquares) # (case)
_make "st(1, ${a[0]-10});" # size.x
_make "st(2, ${a[1]-10});" # size.y
_make "st(3, ${a[2]-0.5});" # smoothness
_make 'st(1, floor(ld(1) * X / W));'
_make 'st(2, floor(ld(2) * (1 - Y / H)));'
_make 'st(4, frand(ld(1), ld(2), 4));' # r
_make 'st(4, ld(4) - (1 - P) * (1 + ld(3)));'
_make 'st(4, smoothstep(0, -ld(3), ld(4), 4));' # m
_make 'mix(A, B, ld(4))'
;;
Here, frand(), smoothstep() and
mix() are pseudo functions. Customizable parameters are
generally stored first. _make is just an expression string
builder function.
xfade-easing.h (custom ffmpeg variant):
static vec4 gl_randomsquares(const XTransition *e)
{
INIT_BEGIN
ARG2(ivec2, size, 10, 10)
ARG1(float, smoothness, 0.5)
INIT_END
float r = frand2(floor2(mul2(vec2i(size), e->p)));
float m = smoothstep(0, -smoothness, r - e->progress * (1 + smoothness));
return mix4(e->a, e->b, m);
}Here, vec4 and ivec2 simulate GLSL vector
types and XTransition encapsulates data pertaining to a
transition:
typedef struct XTransition {
float progress; // transition progress, 0.0 to 1.0 (cf. P)
float ratio; // frame width / height (cf. W / H)
vec2 p; // pixel position, .y==0 is bottom (cf. X, Y)
vec4 a, b; // plane data at p (cf. A, B)
...
} XTransition;And the extended transitions delegate is:
static void xtransition_transition(AVFilterContext *ctx,
const AVFrame *a, const AVFrame *b,
AVFrame *out,
float progress,
int slice_start, int slice_end, int jobnr)
{
const XFadeContext *s = ctx->priv;
const XFadeEasingContext *k = s->k;
const float mw = k->mw, mh = k->mh, mv = k->mv; // as float
XTransition e = { // slice data
.progress = 1 - progress, // 0 to 1 for xtransitions
.ratio = k->r, // pixel ratio
.k = k // common context
};
// pixel iterator and unit interval conversions
for (int y = slice_start; y < slice_end; y++) {
e.p.y = 1 - y / mh; // y=0 is bottom
for (int x = 0, p = 0; x <= k->mw; x++) {
e.p.x = x / mw;
e.a = e.b = VEC4(0, 0.5, 0.5, 1); // plane defaults
do {
e.a.p[p] = line(a, p, y)[x] / mv; // from colour
e.b.p[p] = line(b, p, y)[x] / mv; // to colour
} while (++p < k->n);
vec4 c = k->xtransitionf(&e); // transition colour
do {
--p;
line(out, p, y)[x] = scaleUI(c.p[p], k->mv); // clips
} while (p > 0);
}
}
}gl_InvertedPageCurlThis is transpiled from the InvertedPageCurl GL Transition which originated from the WebVfx WebGL pagecurl shader which is itself based on code by Calyptus Life AB which seems no longer available. The Hewlett-Packard accreditation by Sergey Kosarevsky is obscure but preserved here.
gl_SimplePageCurlThis is adapted from the elegant simple page curl effect
by Andrew Hung who also provides an excellent shader
breakdown to demystify the deformation effect.
It is more versatile than gl_InvertedPageCurl and takes the
following parameters: - angle may be any 360° angle
(horizontal east is 0°, curl direction is angle - 90°
anticlockwise); in effect, 1–89° curls from bottom-right; 91–179°
top-right; 181–269° (or -91–-179°) top-left; 271–359° (or -1–-89°)
bottom-left; 0° curls upwards; 90° left; ±180° down; 270° (or -90°)
right - radius sets the cylinder radius - roll
to roll the turning page into a cylinder
(gl_InvertedPageCurl only rolls) -
reverseEffect to uncurl or unroll - greyBack
to render overleaf greyscale instead of colour - opacity
the underside opacity - shadow the shadow intensity
The main differences from gl_InvertedPageCurl are: -
curling in any direction, not just right-to-left at 30° or 100° - takes
aspect ratio into account, ensuring accurate angle - can render back of
turning page either rolling or just curled over - back of turning page
rendered in colour or greyscale with variable opacity - smaller default
radius of 0.15, not 1/2π
Example: using gl_SimplePageCurl to emulate
gl_InvertedPageCurl
'gl_InvertedPageCurl(30)' vs
'gl_SimplePageCurl(24.8,0.159,1,0,1,0.8,0.1)'
these parameters factor in the aspect ratio (5:4 here), 1/2π radius,
roll effect, greyscale overleaf, and shadowing.
There is barely any noticeable difference, which confirms Mr Hung’s
remark that complex mathematics isn’t needed: just simple trigonometry
and scalar product projections. However gl_SimplePageCurl
takes longer to process than gl_InvertedPageCurl.
Example: gl_SimplePageCurl with various
angle and roll options (Abstract and
Renaissance art by Kandinsky and Titian)
A by-product effect is a wipe transition in any direction, achieved
by setting radius=0,roll=1.
Example: 'gl_SimplePageCurl(160,0,1)' 160°
wipe
gl_SimpleBookCurl(custom ffmpeg only)
This is adapted from gl_SimplePageCurl to clamp the curl
to the virtual ‘spine’ at the horizontal centre, then flatten the radius
to zero, using built-in easing to appear more realistic.
It takes the following parameters: - angle may be any 360°
angle (horizontal east is 0°, curl direction is angle - π/2
anticlockwise); in effect, angles from 180° to 359° (or -1° to -180°)
page backwards - radius sets the cylinder radius -
shadow the shadow intensity
Example: gl_SimpleBookCurl with various
angle and radius values paging forwards and
backwards, rotated and overlaid onto a desk texture (credit: Ethical Corporation
Magazine)
The CLI script can generate random
GLSL-style transitions using the pseudo transition name
gl_random which shuffles all the available transition names
then cycles through them. Random transitions are particularly useful for
slideshows. As the random transition is initially unknown, the
expression options will not work in this case.
For the custom expression variant which cannot take named parameters, customisation parameters are ignored and resort to defaults.
For the custom ffmpeg variant which can take named parameters, a
common background colour or transparency or texture can be set using
background, e.g.:
'gl_random(background=black)' renders a black background
(the default)
'gl_random(background=white)' renders a white
background
'gl_random(background=gray@0.5)' renders a semi-transparent
grey background
'gl_random(background=-1)' renders a transparent
background
'gl_random(background=-10)' renders the texture -10 (a rainbow effect)
and for even more randomness:
'gl_random(background=random)' renders a random background
colour picked by ffmpeg
These affect transitions that take a background parameter
and have no effect on those that do not.
Example: (7 random transitions)
xfade-easing.sh -X -t gl_random -v left.mp4 gl*.png (left,
custom expression)
xfade-easing.sh -t 'gl_random(background=random)' -v right.mp4 gl*.png
(right, custom ffmpeg)
Transitions that affect colour components work differently for RGB
than non-RGB colour spaces and for different bit depths. For the custom
expression variant, xfade-easing.sh emulates
vf_xfade.c
function config_output() by deducing the RGB signal type
AV_PIX_FMT_FLAG_RGB from the -f option format
name (rgb/bgr/etc. see pixdesc.c)
and the bit depth from ffmpeg -pix_fmts data. It can then
set the maximum and mid plane values correctly. See How
does FFmpeg identify color spaces? for details.
The expression files in expr/ cater for RGB and
YUV formats with 8-bit component depth. For faster processing of
greyscale media use xfade-easing.sh -f gray. Greyscale is
not RGB therefore it is processed like a luma plane.
If in doubt, check with ffmpeg -pix_fmts or use the xfade-easing.sh -f option.
These conventions are adopted: - colour values are interpreted
according to sign, magnitude and syntax: - values that match the ffmpeg
Color syntax
are treated as RGBA colour components packed into 32 bits, these are
values greater than 1 (integers) and ffmpeg colour names - values from
0.0 (black) to 1.0 (white) inclusive are an opaque shade of grey -
values from -0.0 (black) to -1.0 (white) are a transparent shade of
grey
(-0, negative zero, is recognised; -1 to -2 exclusive get clamped to -1)
- values -2 or less (integers) select a texture
(default -2) - all background colour parameters are named
background (most GL Transition backgrounds are named
differently)
Consequently a value of exactly 1 is rendered white but 2 (RGBA
#00000002) is almost transparent black. To get R=0,G=0,B=1
specify the colour using hexadecimal notation, #000001.
The custom expression variant only suports transparent white and
opaque grey values, -1 and 0.0 to 1.0, it does not support colour or
textures.
e.g. gl_swap(, , , 0.67) for 67% grey background (other
parameters take default values).
The custom ffmpeg variant supports the full Color syntax
including named colours and variable alpha,
e.g. gl_Stripe_Wipe(color1=DeepSkyBlue, color2=ffd700)
Colour value examples: - CornflowerBlue (a standard X11
colour name, see ffmpeg colour
names) - 0x56789A or #56789A (packed RGB
hexadecimal digits) - 0xA987657F or
#A98765@0.5 or A98765@0x7F (packed RGBA) -
random (a random colour generated by ffmpeg) -
0 (black) - 1 (white) - 0.75 (75%
grey) - bfbfbf7f (75% grey with 50% alpha) -
-0.75 (75% grey with 100% alpha, i.e. transparent) -
-0 (transparent black, negative zero) - -1
(transparent white) - -2 (moving texture number -2) -
-3 (still image version of texture -2) -
12345A is ambiguous and yields 12345 decimal, or
0x00003039
Avoid decimal numbers above 1, e.g. 255 is not blue but opaque black
(RGB #000000FF).
Example: gl_StarWipe transitions with
Lime and -15 (still diamond pattern) border
colours
gl_StarWipe(borderThickness=0.1, borderColor=Lime)
(left)
gl_StarWipe(borderThickness=0.1, borderColor=-15)
(right)
Many transitions reveal areas which are not painted, exposing a
background instead. Unlike GL Transitions which show a black background,
this implementation provides a background parameter which
takes a colour value – grey or RGBA colour or texture or transparent (the custom expression
variant only suports grey and transparent).
Transparent background transitions make for good overlay
effects. Alternatively, use opaque chroma-key
compositing backgrounds, commonly #00B140 for green-screen
and #0047BB or #0827F5 for blue-screen.
(custom ffmpeg only)
[!NOTE] this features is experimental – for generic background effects use the ffmpeg overlay filter in conjunction with transparent xfade-easing.
Textures are shader effects ported from Shadertoy, mainly for transition
backgrounds selected with the background parameter but any
colour parameter can select a texture – see Colour parameters. They are referenced by
a negative index, where - even values show a moving texture with linear
progress (uneased and non-reversed) - odd values show a still version of
its even-valued texture, e.g. -5 is a still of -4 at the halfway
point
-2,-3: Natural
vignetting by ApoorvaJ
-4,-5: glowingMarblingBlack by
nasana
-6,-7: Monochrome
Hyperbola by MichaelPohoreski
-8,-9: Skyline in 132
chars by GregRostami
-10,-11: simple rainbow
formula by Jodie
-12,-13: simple
plasma by Kastor
-14,-15: diamond
pattern by rcread
-16,-17: Glowing
thing by denzen
-18,-19: cinetunnel
by tomviolin
-20,-21: spring time
by bergi
-22,-23: Skyline4 by
FabriceNeyret2
-24,-25: Water
Ripple by liucc09
Example: gl_DirectionalScaled transition with
glowingMarblingBlack background texture and sinusoidal
easing
gl_DirectionalScaled(direction.x=-1,direction.y=1,scale=0.1,background=-4)
Textures are useful for all transitions that take a
background parameter and also for
gl_fadecolor, gl_StarWipe.
Blending involves compositing image layers according to a blending function and opacity formula, whereas normal transition colour mixing performs simple linear interpolation in all planes determined by a weight factor.
One exception is the gl_blend transition (custom ffmpeg
only) which blends the inputs according to its numerical
mode parameter. The first input acts as the backdrop layer
and the second as the source layer. At half the duration, the source is
blended over the backdrop according to the blend mode algorithm. Blend
modes available are the CSS blend modes
which use the standard PDF
1.7 section 11.3.3 compositing formula and blending functions, which
are quite ubiquitous:
To blend transitions over a backdrop layer use the blend filter.
It is poorly documented and the implementation in vf_blend.c,
vf_blend_init.h,
blend_modes.c
is quite basic: it does not render standard blending as above because it
seems to be designed for premultiplied alpha, achieved for RGB
using
geq="r='r(X,Y)*alpha(X,Y)/255':g='g(X,Y)*alpha(X,Y)/255':b='b(X,Y)*alpha(X,Y)/255'"
before blend.
Example: four all_mode blend options for
gl_StereoViewer transitions with 50% grey
transparency
gl_StereoViewer(zoom=0.6,radius=0.3,flip=1,background=-0.5)
The expression files in expr/ cater for RGBA and YUVA transparency formats in 4 planes as well as opaque RGB and YUV in 3 planes and mono/gray in 1 plane.
For lossless intermediate video content with alpha channel support
use the xfade-easing.sh -v -f
options with an alpha format, e.g. rgba or
yuva420p, and .mkv filename extension.
For lossy video with alpha use an alpha format and the .webm extension. Note: webm encoding is extremely slow and webm alpha is not widely supported.
To specify alpha in transition parameters, see Colour parameters.
Example: overlaid transparent
gl_RotateScaleVanish transition with
quadratic-in easing
ffmpeg -i skaro.png -i tardis.png -i gallifrey.png -filter_complex "
[0]loop=-1:1, fps=25, format=rgba, scale=250:-2[a];
[1]loop=-1:1, fps=25, format=rgba, scale=250:-2[b];
[2]loop=-1:1, fps=25, format=rgb24, scale=250:-2[bg];
[a][b]xfade=offset=1:duration=3:easing=quadratic-in:transition='gl_RotateScaleVanish(FadeInSecond=0,ReverseEffect=1,trkMat=1)'[fg];
[bg][fg]overlay" -t 5 -c:v libx264 -pix_fmt yuv420p drwho.mp4
or using the CLI script
xfade-easing.sh -f rgba -e quadratic-in -t 'gl_RotateScaleVanish(FadeInSecond=0,ReverseEffect=1,trkMat=1)' -v alpha.mkv -z 250x skaro.png tardis.png
ffmpeg -i gallifrey.png -i alpha.mkv -filter_complex '[0]scale=250:-2[b]; [b][1]overlay' drwho.mp4
This demonstrates the additional trkMat parameter which
tracks the Tardis alpha value to expose Skaro behind, then Gallifrey’s
Citadel when the transition ends, both planets being opaque
images.
(trkMat is only availble in the custom ffmpeg variant)
Transition gl_StereoViewer also has a
trkMat parameter for clean cutout effects.
See also the example under Transition
gl_SimpleBookCurl which overlays a transparent
transition.
(custom ffmpeg only)
The generic xfade reverse option reverses the transition
and/or easing effects. It takes a bitmapped number:
0 no reverse (default)1 swap the inputs and reverse the transition
effect2 reverse the easing effect3 swap the inputs and reverse the transition and easing
effectsIt is necessary to swap the inputs during the reversed transition to
match the inputs before and after the transition, i.e. during the
offset time and after the
offset+duration time. This is tricky but not
impossible to implement in the xfade-easing.sh
script for custom expressions. The script has a -b option
to pass reverse values through to xfade for the custom ffmpeg
variant.
Most standard xfade transitions have reversed equivalents,
e.g. wipeleft and wiperight, but few GLSL
transitions do. Unlike standard easings, CSS easings also have no
mirror-image reversal mode.
Example: using the same CSS Linear coefficients as above
easing: 'linear(0, 0.5 30%, 0.2 60% 80%, 1)', transition:
gl_FanUp, reverse: 0–3
There is no gl_FanDown transition but reversing
gl_FanUp provides one. Reversing is also particurly useful
for squeezeh, squeezev,
gl_BookFlip, gl_BowTie, gl_cube,
gl_doorway, gl_heart,
gl_pinwheel, gl_rotateTransition,
gl_Slides, gl_Swirl, gl_swap,
gl_windowslice.
This is a powerful feature that considerably increases the number of transitions available.
FFmpeg incorporates a simple arithmetic expression evaluator
implemented as a LL(1) recursive descent parser. Custom
expr strings initially get parsed into an expression tree
of AVExpr nodes in libavutil/eval.c
which is then executed for every pixel in each plane. The inefficiency
is further exacerbated by disabling threading in order to use the
st() and ld() state variables shared between
slices (a slice is a range of frame lines processed by a thread job).
But modern CPU speeds make Xfade custom transitions a viable option, if
somewhat challenging.
The following plots show empirical processing times for a 3-second transition of 3-plane frames (rgb24) through a null muxer in SD (720x480) and HD (1280x720) sizes on an otherwise idle Intel 2018 Mac, taking the minimum times of several runs.
Based on benchmark scores (Geekbench Mac Benchmark Chart) these times are representative of 2017-2020 Intel Macs, but scale them as follows:
Mac model performance varies enormously so timing vintage Macs is very approximate. Windows performance has not been measured.
| Name | SD | HD |
|---|---|---|
| gl_Bars | 10.47 | 27.06 |
| gl_BookFlip | 14.21 | 36.69 |
| gl_Bounce | 27.75 | 73.06 |
| gl_CornerVanish | 6.89 | 17.57 |
| gl_CrazyParametricFun | 57.69 | 156.65 |
| gl_CrossOut | 16.15 | 42.36 |
| gl_Diamond | 8.59 | 22.1 |
| gl_DirectionalScaled | 39.77 | 105.64 |
| gl_DoubleDiamond | 10.99 | 28.15 |
| gl_Dreamy | 31.46 | 83.57 |
| gl_FanIn | 12.54 | 32.85 |
| gl_FanOut | 12.59 | 33.23 |
| gl_FanUp | 9.99 | 25.61 |
| gl_Flower | 29.6 | 77.89 |
| gl_InvertedPageCurl | 47.65 | 126.94 |
| gl_LinearBlur | 516.2 | 1385.06 |
| gl_Mosaic | 72.47 | 189.77 |
| gl_PolkaDotsCurtain | 22.13 | 58.39 |
| gl_Rolls | 36.2 | 93.34 |
| gl_RotateScaleVanish | 42.29 | 111.61 |
| gl_SimplePageCurl | 122.08 | 315.73 |
| gl_Slides | 18.91 | 49.39 |
| gl_StarWipe | 48.83 | 122.7 |
| gl_Swirl | 41.9 | 118.08 |
| gl_WaterDrop | 21.12 | 56.13 |
| gl_angular | 16.18 | 42.2 |
| gl_cannabisleaf | 28.54 | 76.95 |
| gl_chessboard | 16.89 | 44.15 |
| gl_crosshatch | 52.07 | 140.25 |
| gl_crosswarp | 35.92 | 92.72 |
| gl_cube | 65.88 | 178.62 |
| gl_directionalwarp | 62.48 | 166.27 |
| gl_doorway | 39.93 | 108.59 |
| gl_heart | 16.7 | 44.91 |
| gl_hexagonalize | 66.04 | 172.6 |
| gl_kaleidoscope | 174.98 | 475.29 |
| gl_perlin | 90.86 | 239.03 |
| gl_pinwheel | 11.67 | 30.72 |
| gl_polar_function | 17.85 | 45.79 |
| gl_powerKaleido | 532.58 | 1431.74 |
| gl_randomNoisex | 10.23 | 26.4 |
| gl_randomsquares | 24.21 | 64.17 |
| gl_ripple | 35.57 | 94.07 |
| gl_rotateTransition | 35.41 | 95.13 |
| gl_rotate_scale_fade | 56.07 | 151.46 |
| gl_squareswire | 50.12 | 134.43 |
| gl_static_wipe | 53.73 | 143.37 |
| gl_swap | 71.96 | 186.63 |
| gl_windowblinds | 21.3 | 57.18 |
| gl_windowslice | 16.93 | 43.22 |
This shows most GL transitions take around half a minute for SD and a minute and a half for HD, however some take considerably longer (off-scale transitions shown in italics).
See xfade-easing.h for the C code transpiled from GLSL that helped to optimize the custom expressions. See the files in glsl/ refactored from other GLSL transition sources that were used for intermediate testing in the GL Transition Editor.
| Name | SD | HD |
|---|---|---|
| circleclose | 16.03 | 41.19 |
| circlecrop | 11.6 | 30.27 |
| circleopen | 16.12 | 41.54 |
| coverdown | 10.38 | 27.2 |
| coverleft | 10.17 | 26.53 |
| coverright | 10.46 | 28.36 |
| coverup | 10.24 | 26.98 |
| diagbl | 11.58 | 29.99 |
| diagbr | 12.72 | 31.89 |
| diagtl | 11.05 | 28.47 |
| diagtr | 11.89 | 30.5 |
| dissolve | 9.92 | 25.74 |
| fade | 2.41 | 5.72 |
| fadeblack | 24.56 | 65.86 |
| fadefast | 8.21 | 21.8 |
| fadegrays | 34.06 | 92.03 |
| fadeslow | 8.19 | 21.58 |
| fadewhite | 24.34 | 63.91 |
| hlslice | 14.96 | 39.14 |
| hlwind | 18.86 | 50.53 |
| horzclose | 11.17 | 29.09 |
| horzopen | 10.9 | 28.32 |
| hrslice | 15.86 | 40.73 |
| hrwind | 18.24 | 47.72 |
| pixelize | 27.83 | 73.49 |
| radial | 13.51 | 35.8 |
| rectcrop | 8.03 | 20.63 |
| revealdown | 10.48 | 27.46 |
| revealleft | 10.05 | 26.84 |
| revealright | 10.57 | 27.38 |
| revealup | 10.11 | 26.9 |
| slidedown | 11.68 | 30.38 |
| slideleft | 11.1 | 29.47 |
| slideright | 11.57 | 30.82 |
| slideup | 11.33 | 29.26 |
| smoothdown | 10.57 | 27.7 |
| smoothleft | 9.68 | 26.14 |
| smoothright | 10.56 | 28.07 |
| smoothup | 9.87 | 25.91 |
| squeezeh | 6.7 | 17.07 |
| squeezev | 6.62 | 17.31 |
| vdslice | 15.61 | 40.1 |
| vdwind | 18.19 | 47.95 |
| vertclose | 11.04 | 29.15 |
| vertopen | 11.49 | 28.5 |
| vuslice | 14.78 | 39.07 |
| vuwind | 18.69 | 49.33 |
| wipebl | 3.77 | 9.26 |
| wipebr | 4.22 | 10.29 |
| wipedown | 2.52 | 5.71 |
| wipeleft | 2.11 | 4.88 |
| wiperight | 2.54 | 5.98 |
| wipetl | 3.37 | 8.21 |
| wipetr | 3.72 | 9.04 |
| wipeup | 2.06 | 4.68 |
| zoomin | 29.09 | 76.4 |
These are only used when easing standard Xfade transitions.
| Name | SD | HD |
|---|---|---|
| back | 7.26 | 18.44 |
| bounce | 14.18 | 36.07 |
| circular | 6.34 | 15.55 |
| cuberoot | 6.79 | 17.1 |
| cubic | 6.52 | 16.76 |
| elastic | 11.49 | 29.41 |
| exponential | 6.44 | 16.44 |
| linear | 2.64 | 6.22 |
| quadratic | 4.6 | 11.37 |
| quartic | 6.66 | 16.72 |
| quintic | 6.53 | 16.75 |
| sinusoidal | 5.26 | 13.33 |
| squareroot | 4.71 | 12.02 |
If easing is used these easing times must be added to the GL or Xfade transition times above.
| Name | SD | HD |
|---|---|---|
| circleclose | 0.94 | 1.71 |
| circlecrop | 0.7 | 1.19 |
| circleopen | 0.94 | 1.72 |
| coverdown | 0.49 | 0.54 |
| coverleft | 0.73 | 1.18 |
| coverright | 0.72 | 1.16 |
| coverup | 0.49 | 0.54 |
| diagbl | 0.82 | 1.41 |
| diagbr | 0.84 | 1.46 |
| diagtl | 0.83 | 1.39 |
| diagtr | 0.84 | 1.45 |
| dissolve | 0.98 | 1.91 |
| distance | 1 | 1.97 |
| fade | 0.5 | 0.58 |
| fadeblack | 0.61 | 0.72 |
| fadefast | 2 | 5 |
| fadegrays | 1.02 | 1.83 |
| fadeslow | 1.91 | 4.74 |
| fadewhite | 0.61 | 0.72 |
| hblur | 0.7 | 1.17 |
| hlslice | 0.83 | 1.41 |
| hlwind | 0.89 | 1.51 |
| horzclose | 0.73 | 1.14 |
| horzopen | 0.73 | 1.18 |
| hrslice | 0.83 | 1.45 |
| hrwind | 0.86 | 1.48 |
| pixelize | 0.83 | 1.33 |
| radial | 1.07 | 2.08 |
| rectcrop | 0.57 | 0.76 |
| revealdown | 0.49 | 0.54 |
| revealleft | 0.72 | 1.17 |
| revealright | 0.73 | 1.17 |
| revealup | 0.5 | 0.54 |
| slidedown | 0.51 | 0.56 |
| slideleft | 0.73 | 1.17 |
| slideright | 0.76 | 1.14 |
| slideup | 0.51 | 0.56 |
| smoothdown | 0.76 | 1.18 |
| smoothleft | 0.81 | 1.3 |
| smoothright | 0.81 | 1.33 |
| smoothup | 0.73 | 1.17 |
| squeezeh | 0.47 | 0.52 |
| squeezev | 0.71 | 1.15 |
| vdslice | 0.74 | 1.16 |
| vdwind | 1.22 | 2.53 |
| vertclose | 0.79 | 1.35 |
| vertopen | 0.82 | 1.38 |
| vuslice | 0.74 | 1.15 |
| vuwind | 1.22 | 2.46 |
| wipebl | 0.5 | 0.63 |
| wipebr | 0.5 | 0.62 |
| wipedown | 0.44 | 0.5 |
| wipeleft | 0.49 | 0.62 |
| wiperight | 0.5 | 0.61 |
| wipetl | 0.5 | 0.63 |
| wipetr | 0.5 | 0.64 |
| wipeup | 0.44 | 0.5 |
| zoomin | 0.92 | 1.56 |
| gl_Bars | 1.25 | 2.78 |
| gl_BookFlip | 1.28 | 2.74 |
| gl_Bounce | 1.62 | 3.65 |
| gl_BowTie | 1.46 | 3.46 |
| gl_ButterflyWaveScrawler | 5.23 | 15.29 |
| gl_CornerVanish | 1.04 | 2.19 |
| gl_CrazyParametricFun | 2.91 | 7.51 |
| gl_CrossOut | 1.13 | 2.29 |
| gl_CrossZoom | 45.73 | 131.66 |
| gl_Diamond | 1.11 | 2.25 |
| gl_DirectionalScaled | 2.56 | 6.39 |
| gl_DoubleDiamond | 1.11 | 2.35 |
| gl_Dreamy | 2.41 | 6.2 |
| gl_EdgeTransition | 9.96 | 31.12 |
| gl_Exponential_Swish | 5.12 | 14.38 |
| gl_FanIn | 1.23 | 2.67 |
| gl_FanOut | 1.28 | 2.79 |
| gl_FanUp | 1.22 | 2.7 |
| gl_Flower | 1.78 | 4.13 |
| gl_GridFlip | 2.15 | 5.28 |
| gl_InvertedPageCurl | 2.02 | 4.83 |
| gl_LinearBlur | 32.78 | 97.77 |
| gl_Lissajous_Tiles | 84.81 | 235.19 |
| gl_Mosaic | 2.85 | 7.01 |
| gl_PolkaDotsCurtain | 1.38 | 3.07 |
| gl_Rolls | 1.57 | 3.63 |
| gl_RotateScaleVanish | 1.96 | 4.65 |
| gl_SimpleBookCurl | 2.19 | 5.52 |
| gl_SimplePageCurl | 1.69 | 3.85 |
| gl_Slides | 1.25 | 2.59 |
| gl_StageCurtains | 1.98 | 4.79 |
| gl_StarWipe | 2.32 | 5.73 |
| gl_StereoViewer | 2.25 | 5.79 |
| gl_Stripe_Wipe | 2.69 | 6.71 |
| gl_Swirl | 3.01 | 7.49 |
| gl_WaterDrop | 1.94 | 4.64 |
| gl_angular | 1.46 | 3.33 |
| gl_blend | 1.76 | 4.44 |
| gl_cannabisleaf | 3.28 | 8.63 |
| gl_chessboard | 1.14 | 2.32 |
| gl_crosshatch | 2.12 | 5.04 |
| gl_crosswarp | 1.98 | 4.77 |
| gl_cube | 1.82 | 4.22 |
| gl_directionalwarp | 2.64 | 6.58 |
| gl_doorway | 1.61 | 3.58 |
| gl_fadecolor | 1.38 | 3.2 |
| gl_heart | 1.11 | 2.35 |
| gl_hexagonalize | 2.34 | 5.74 |
| gl_kaleidoscope | 5.37 | 15.54 |
| gl_morph | 1.87 | 4.51 |
| gl_perlin | 2.89 | 6.98 |
| gl_pinwheel | 1.48 | 3.42 |
| gl_polar_function | 1.88 | 4.48 |
| gl_powerKaleido | 5.45 | 15.51 |
| gl_randomNoisex | 1.7 | 3.85 |
| gl_randomsquares | 1.96 | 4.61 |
| gl_ripple | 2.44 | 5.93 |
| gl_rotateTransition | 2.27 | 5.44 |
| gl_rotate_scale_fade | 2.52 | 6.32 |
| gl_squareswire | 1.34 | 2.89 |
| gl_static_wipe | 1.96 | 4.63 |
| gl_swap | 1.71 | 3.78 |
| gl_windowblinds | 1.27 | 2.65 |
| gl_windowslice | 1.17 | 2.37 |
This plot combines both Xfade and GL transitions.
It shows most transitions take around a second for SD and 2.5 seconds for HD but again some take considerably longer (off-scale transitions shown in italics).
Progress easing is calculated once per slice and presents no discernable performance hit.
The custom ffmpeg C code in xfade-easing.h deploys a single pixel
iterator for all extended transition functions which in turn operate on
all planes at once, and it does not require
-filter_complex_threads 1. Use is made of aligned variables
and inlined code snippets as compiler hints for fast SIMD vector and
sincos instructions.
Other, faster ways to use GL Transitions with FFmpeg are: - gl-transition-scripts
includes a Node.js CLI script gl-transition-render which
can render multiple GL Transitions and images for FFmpeg processing - ffmpeg-concat
is a Node.js package which requires installation and a lot of temporary
storage - ffmpeg-gl-transition
is a native FFmpeg filter which requires building ffmpeg from source
xfade-easing.sh is a Bash 4 shell
wrapper for ffmpeg. It can: - generate custom easing and transition
expressions for the xfade expr parameter - generate easing
graphs via gnuplot (especially useful for CSS easings) - create eased
transition videos from two visual media sources - concenate multiple
visual media with eased transitions for presentations and
slideshows.
FFmpeg XFade easing and extensions version 3.6.2 by Raymond Luckhurst, https://scriptit.uk
Wrapper script to render eased XFade/GLSL transitions natively or with custom expressions.
Generates easing and transition expressions for xfade and for easing other filters.
Also creates easing graphs, demo videos, presentations and slideshows.
See https://github.com/scriptituk/xfade-easing
Usage: xfade-easing.sh [options] [image/video inputs]
Options:
-t transition name and arguments, if any (default: fade); use -L for list
args in parenthesis as CSV, e.g.: gl_perlin(5,0.1) (both variants)
or key=value pairs, e.g.: gl_perlin(smoothness=0.1, scale=5) (custom ffmpeg only)
use gl_random to cycle through shuffled transitions ported from GLSL
-e easing function and arguments, if any (default: linear)
CSS args in parenthesis as CSV, e.g.: cubic-bezier(0.17,0.67,0.83,0.67)
-b reverse transition and/or easing effect (custom ffmpeg only) (default: 0)
1 reverses the inputs and transition effect; 2 reverses the easing; 3 reverses both
-x expr output filename (default: no expr), accepts expansions, - for stdout
-a append to expr output file
-s expr output format string with text expansion (default: '%x')
%f expands to pixel format, %F to format in upper case
%e expands to the easing name
%t expands to the transition name
%E, %T upper case expansions of %e, %t
%c expands to the CSS easing arguments
%a expands to the GL transition arguments; %A to the default arguments (if any)
%x expands to the generated expr, condensed, intended for inline filterchains
%X uncondensed version of %x, intended for -/filter_complex script files
%p expands to the progress easing expression, condensed, for inline filterchains
%g expands to the generic easing expression (for other filters), condensed
%z expands to the eased transition expression only, condensed
for the uneased transition expression only, omit -e option and use %x or %X
%P, %G, %Z, uncondensed versions of %p, %g, %z, for -/filter_complex script files
%n inserts a newline
-p easing plot filename (default: no plot), accepts expansions
formats: gif, jpg, png, svg, pdf, eps, html <canvas>, from file extension
-m multiple easings to plot on one graph (default: the -e easing)
CSV easings with optional legend prefix, e.g. in=cubic-in,out=cubic-out,in-out=cubic
-q plot title (default: easing name, or Easings for multiple plots)
-c canvas size for easing plot (default: 640x480, scaled to inches for PDF/EPS)
format: WxH; omitting W or H keeps aspect ratio, e.g. -z x300 scales W
-v video output filename (default: no video), accepts expansions
formats: mkv (FFV1), mp4 (H264), webm (VP9), raw
animated formats: webp (VP8), png (APNG), gif, y4m (yuv4mpeg)
from file extension; if filename is - then format is the null muxer (no output)
if -f format has alpha then mkv,webm,raw generate transparent video output
if gifski is installed then gifs will be optimised
raw decode: ffmpeg -f rawvideo -pixel_format f -framerate r -video_size s -i ...
-o additional ffmpeg options, e.g. -o '-movflags +faststart' for MP4 Faststart
-r video framerate (default: 25fps)
-f pixel format (default: rgb24): use ffmpeg -pix_fmts for list
-z video size (default: input 1 size)
format: WxH; omitting W or H keeps aspect ratio, e.g. -z 400x scales H
-d video transition duration (default: 3s, minimum: 0) (see note after -l)
-i time between video transitions (default: 1s, minimum: 0) (see note after -l)
-l video length (default: 5s)
note: options -d, -i, -l are interdependent: l=ni+(n-1)d for n inputs
given -t & -l, d is calculated; else given -l, t is calculated; else l is calculated
-j allow input videos to play within transitions (default: no)
normally videos only play during the -i time but this sets them playing throughout
-n show effect name on video as text (requires the libfreetype library)
-u video text font size multiplier (default: 1.0)
-k video stack orientation,gap,colour,padding (default: ,0,white,0), e.g. h,2,red,1
stacks uneased and eased videos horizontally (h), vertically (v) or auto (a)
auto selects the orientation that displays easing to best effect
also stacks transitions with default and custom parameters, eased or not
videos are only stacked if they are different (nonlinear-eased or customised)
unstacked videos can be padded using orientation=1, e.g. 1,0,blue,5
-L list all transitions and easings
-H show this usage text
-V show this script version
-X use custom expressions, not the xfade API that supports xfade-easing natively
by default native support is detected automatically using ffmpeg --help filter=xfade
the native API adds easing and reverse options and runs much faster
e.g. xfade=duration=4:offset=1:easing=quintic-out:transition=wiperight
e.g. xfade=duration=5:offset=2:easing='cubic-bezier(.17,.67,.83,.67)' \
:transition='gl_swap(depth=5,reflection=0.7,perspective=0.6)' (see repo README)
-I set ffmpeg loglevel to info for -v (default: warning), also dumps ffmpeg command
-D dump debug messages to stderr and set ffmpeg loglevel to debug for -v
-P log xfade progress percentage using custom expression print() function (implies -I)
-T temporary file directory (default: /tmp)
-K keep temporary files if temporary directory is not /tmp
Notes:
1. point the shebang path to a bash4 location (defaults to MacPorts install)
2. this script requires Bash 4 (2009), ffmpeg, ffprobe, gawk, gsed, seq
also gnuplot for plots, gifski for optimised animated gifs
3. use ffmpeg option -filter_complex_threads 1 (slower) because xfade expression
vars used by st() & ld() are shared across slices, therefore not thread-safe
(the custom ffmpeg build works without -filter_complex_threads 1)
4. CSS easings are supported in the custom ffmpeg build but not as custom expressions
4. certain xfade transitions are not implemented as custom expressions because
they perform aggregation (distance, hblur)
5. many GLSL transitions are also ported, some of which take customisation parameters
to override defaults append parameters in parenthesis (see -t option)
6. certain GLSL transitions are only available in the custom ffmpeg build
7. many transitions do not lend themselves well to easing, others have built-in easing
easings that overshoot (back & elastic) may cause weird effects
Expr code is generated using the -x option and
customised with the -s,-a,-f
options.
xfade-easing.sh -t slideright -e quadratic -x -xfade-easing.sh -t coverup -e quartic-in -x coverup_quartic-in.txtxfade-easing.sh -t coverup -e quartic-in -x %t_%e.txtxfade-easing.sh -t rectcrop -e exponential-out -s "\$expr['%t+%e'] = '%n%X';" -x exprs.php -a$expr['rectcrop+exponential-out'] = '
st(0, if(lte(P, 0), 0, 2^(10 * P - 10)))
;
st(1, abs(ld(0) - 0.5));
if(lt(abs(X - W / 2), ld(1) * W) * lt(abs(Y - H / 2), ld(1) * H),
if(lt(ld(0), 0.5), B, A),
ifnot(3-PLANE, 255)
)';xfade-easing.sh -t gl_polar_function -s "expr='%n%X'" -x fc-script.txtP directly:expr='
st(1, 5);
st(2, X / W - 0.5);
st(3, 0.5 - Y / H);
st(4, atan2(ld(3), ld(2)) - PI / 2);
st(4, cos(ld(1) * ld(4)) / 4 + 1);
st(1, hypot(ld(2), ld(3)));
if(gt(ld(1), ld(4) * (1 - P)), A, B)'
Plots are generated using the -p option and customised
with the -m,-q,-c options.
Plot data is logged using the print function of the
ffmpeg expression evaluator for the first plane and first pixel as xfade
progress P goes from 1 to 0 at 100fps.
xfade-easing.sh -e elastic -p plot-%e.pdfxfade-easing.sh -q 'Bounce Easing' -m in=bounce-in,out=bounce-out,in-out=bounce -p %e.png -c 500x
The plots above in Standard easings show test plots for all standard easings and all three modes (in, out and in-out).
Videos are generated using the -v option and customised
with the
-b,-r,-f,-z,-d,-i,-l,-j,-n,-u,-k,-o
options.
[!NOTE] all transition effect demos on this page are animated GIFs regardless of the commands shown
Input media is serialised according to the expression L = NI + (N − 1)D
where L is the total video
length (option -l); I is the individual display time
(option -i); D is
the transition duration (option -d); N is the number of inputs.
Transition offsets are spaced accordingly. Depending on option
-j and the input media length, pre and post padding is
added by frame cloning to ensure enough frames are available for
transition processing. See Usage for the precedence
of options -l, -i, -d.
xfade-easing.sh -t hlwind -e quintic-in -v windy.gif
creates an animated GIF image of the hlwind transition with quintic-in
easing using default built-in images

xfade-easing.sh -t fadeblack -e circular -v maths.mp4 dot.png cross.png
creates a MP4 video of the fadeblack transition with circular easing
using specified inputs (credit: Math & Science Tutor)

xfade-easing.sh -t coverdown -e bounce-out -v %t-%e.mp4 wallace.png shaun.png
creates a video of the coverdown transition with bounce-out easing using
expansion specifiers for the output file name

xfade-easing.sh -t 'gl_polar_function(25)' -v paradise.mkv -n -u 1.2 islands.png rainbow.png
creates a lossless (FFV1) video (e.g. for further processing) of an
uneased polar_function GL transition with 25 segments annotated in
enlarged text

xfade-easing.sh -t 'gl_Lissajous_Tiles(16,20,0.3,9,3,1,0.8,3,2,Lavender)' -e quadratic -v lissajous.mp4 titian.png kandinsky.png
creates a stunning Lissajous
effect quadratic-eased against a Lavender background demonstrating
extensive use of transition parameters

xfade-easing.sh -t 'gl_cube(,,,,-23)' -e 'cubic-bezier(0.5,0.9,0.5,0.1)' -v cube.mp4 -b 1 BBC_Test_Card_C.png BBC_Test_Card_J.png
creates a reversed cube GL transition against a texture background with cubic-bezier easing that
slows down the middle movement

xfade-easing.sh -t 'gl_StageCurtains(Purple,30,0.05)' -v stage.mp4 -i 1 -d 5 hamlet-players.png hamlet-yorick.png hamlet-fight.png
creates a slow stage curtain effect GL transition with default linear
easing showcasing three scenes from Hamlet

xfade-easing.sh -t 'gl_angular(270,1)' -e exponential -v multiple.mp4 -n -k h -l 20 street.png road.png flowers.png bird.png barley.png
creates a video of the angular GL transition with parameter
startingAngle=270 (south) and clockwise=1 (an
added parameter) for 5 inputs with fast exponential easing

xfade-easing.sh -t gl_BookFlip -e quartic-out -v book.mp4 -f gray -z 248x -n -k h,2,black,1 alice12.png alice34.png
creates a simple greyscale page turn with quartic-out easing for a more
realistic effect.

xfade-easing.sh -t circlecrop -e sinusoidal -v home-away.mp4 -l 10 -d 8 -z 246x -k h,4,LightSkyBlue,2 -n phone.png beach.png
creates a 10s video with a slow 8s circlecrop xfade transition with
sinusoidal easing, horizontally stacked with a 4px
LightSkyBlue gap (see Color) and 2px
padding

xfade-easing.sh -t gl_InvertedPageCurl -e cubic-in -v score.mp4 -f gray -i 2 -d 3 -z 480x -k 1,0,0xD8D8D8,10 fugue1.png fugue2.png fugue3.png
a 3s page curl effect, static for 2s, with cubic-in easing using
greyscale format (-k 1,0,colour,padding creates a
border)

🎹 I play this Bach
fugue on my YouTube channel digitallegro but
the GL InvertedPageCurl there was generated by ffmpeg-concat
xfade-easing.sh -t 'gl_PolkaDotsCurtain(10,0.5,0)' -e 'cubic-bezier(0.4,1.2,0.6,-1.1)' -v life.mp4 -l 7 -d 5 -z 500x -f yuv420p -r 30 balloons.png fruits.png
a GL transition with arguments and cubic-bezier easing, running at 30fps
for 7 seconds, processing in YUV (Y’CbCr) colour space throughout
