Skip to content

Coming from FFmpeg

This content is for v0.4. Switch to the latest version for up-to-date documentation.

If you build videos with FFmpeg filtergraphs, this page shows the alpha2 way to do the same things, and where each tool fits.

alpha2 doesn’t replace FFmpeg: the native programs decode and encode with FFmpeg 8. What changes is the middle. Instead of a filtergraph string, you write a project document, a JSON timeline of sources, tracks and clips that a GPU engine renders. The same document previews in a browser and renders on a server, and you can edit it, diff it and check it against a schema.

FFmpeg alpha2
Inputs (-i) sources, each with an id. Files are given at export time: --asset <id>=<path>.
A filtergraph of labelled streams tracks of clips on one timeline. Later tracks draw on top.
Per-input timestamps, -ss, -t, trim A clip’s timeline_range (where it plays) and source_range (what part of the source plays), in ticks
Output size from the last filter The project’s settings.width and height. Every source is fitted into it.
Pixel coordinates (x=W-w-40) Normalized positions: 0 to 1 across the canvas
Filters on a stream Effects in a composition graph on a clip
-filter_complex with sendcmd or expressions over t Keyframes on any animatable value

One project with a crossfade between two photos, a grade on the second, and a line of text, the FFmpeg command beside it:

Terminal window
ffmpeg -loop 1 -t 3 -i coast.jpg -loop 1 -t 3 -i forest.jpg -filter_complex "
[0:v]scale=1280:720,setsar=1[a];
[1:v]scale=1280:720,setsar=1,eq=contrast=1.2:saturation=1.3[b];
[a][b]xfade=transition=fade:duration=1:offset=2,
drawtext=fontfile=HankenGrotesk.ttf:text='Day two':fontsize=58:fontcolor=white:
x=w*0.05:y=h*0.92-th:enable='gte(t,3)'" -r 30 out.mp4
{
"schema_version": 1,
"id": "from-ffmpeg",
"name": "From FFmpeg",
"settings": {
"width": 1280,
"height": 720,
"frame_rate": {
"num": 30,
"den": 1
},
"sample_rate": 48000
},
"sources": [
{
"id": "coast",
"locator": "asset://coast",
"kind": "image"
},
{
"id": "forest",
"locator": "asset://forest",
"kind": "image"
},
{
"id": "caption",
"kind": "generated",
"generator": {
"type": "text",
"text": "Day two",
"style": {
"font_family": "Hanken Grotesk",
"font_size": 0.08,
"align": "start",
"position": {
"x": 0.05,
"y": 0.92
},
"anchor": {
"x": 0,
"y": 1
}
}
}
}
],
"compositions": [
{
"id": "graded",
"output": "eq",
"nodes": [
{
"id": "clip",
"op": {
"type": "clip"
}
},
{
"id": "eq",
"op": {
"type": "effect",
"input": "clip",
"effect": {
"id": "alpha2.effect.color-controls",
"version": 1,
"params": {
"contrast": {
"mode": "constant",
"value": {
"type": "float",
"value": 1.2
}
},
"saturation": {
"mode": "constant",
"value": {
"type": "float",
"value": 1.3
}
}
}
}
}
}
]
}
],
"timeline": {
"tracks": [
{
"id": "v1",
"name": "Picture",
"kind": "video",
"clips": [
{
"id": "a",
"source_id": "coast",
"timeline_range": {
"start": 0,
"end": 360000
},
"source_range": {
"start": 0,
"end": 360000
}
},
{
"id": "b",
"source_id": "forest",
"timeline_range": {
"start": 240000,
"end": 600000
},
"source_range": {
"start": 0,
"end": 360000
},
"composition": {
"id": "graded"
}
}
],
"transitions": [
{
"from_clip": "a",
"to_clip": "b",
"timeline_range": {
"start": 240000,
"end": 360000
},
"easing": {
"type": "linear"
},
"effect": {
"id": "alpha2.transition.fade",
"version": 1
}
}
]
},
{
"id": "text",
"name": "Text",
"kind": "video",
"clips": [
{
"id": "caption-clip",
"source_id": "caption",
"timeline_range": {
"start": 360000,
"end": 600000
},
"source_range": {
"start": 0,
"end": 240000
}
}
],
"transitions": []
}
]
}
}
Terminal window
alpha2-host export --project graded.json \
--asset coast=coast.jpg --asset forest=forest.jpg \
--font HankenGrotesk.ttf --out out.mp4
Terminal window
[a][b]xfade=transition=fade:duration=1:offset=2

The two clips overlap on one track from 2 s to 3 s, and a transition in the track’s transitions joins them over that range. effect.id names one of 122 built-in transitions (alpha2.transition.fade, directional-wipe, circle-open and so on), and easing shapes its progress. Unlike offset, the range is explicit on the timeline, and both clips keep their own times.

Terminal window
eq=contrast=1.2:saturation=1.3

The graded composition runs the second clip through alpha2.effect.color-controls with contrast 1.2 and saturation 1.3. It also has exposure, temperature and tint. The math is the engine’s own, so the same numbers give a similar look to eq, not the same pixels. levels, lift-gamma-gain, hue-saturation and lut-3d (a .cube file) cover the rest of FFmpeg’s color filters.

Terminal window
drawtext=fontfile=HankenGrotesk.ttf:text='Day two':fontsize=58:x=w*0.05:y=h*0.92-th:enable='gte(t,3)'

Text is a generated source with a text generator, shown by a clip from 3 s to 5 s, which replaces enable. font_size is a fraction of the picture’s shorter side (0.08 × 720 ≈ 58 pixels). position places the text’s anchor point: anchor (0, 1) is its bottom-left corner, so this is x=w*0.05:y=h*0.92-th. Lines are aligned within the text’s wrapping width, max_width (0.9 of the canvas width by default), so align: "start" keeps them at the left. The font comes from --font, as fontfile does. For boxes that size themselves around text, use a layout generator.

Terminal window
[1:v]scale=iw*0.32:-1[pip];[0:v][pip]overlay=W-w-50:H-h-50

A clip on a higher track draws over the ones below. Its visual.scale scales it from its fitted size, and position places its anchor point. The picture-in-picture recipe has the full project, with rounded corners and a shadow, which take more than one filter in FFmpeg.

scale, pad and crop → fitting the canvas

Section titled “scale, pad and crop → fitting the canvas”

Every source is first fitted inside the canvas, centred, keeping its aspect ratio, like scale=...:force_original_aspect_ratio=decrease,pad=.... The bars are the project’s settings.background color (black by default). To fill the frame instead (force_original_aspect_ratio=increase,crop=...), scale the clip up and move it with position: see Vertical video.

alpha2.effect.crop cuts edges off without changing the frame size: top_left and bottom_right are how much to cut from each side, as fractions, with a feather. A clip’s masks cut it to any shape.

FFmpeg alpha2
concat Clips one after another on one track
fade=in, fade=out opacity keyframes on a clip, or a transition
setpts, atempo A source_range longer or shorter than the timeline_range: the clip plays faster or slower, its sound resampled, so its pitch follows the speed
reverse "time_map": { "type": "reverse" }
boxblur, gblur alpha2.effect.gaussian-blur
unsharp alpha2.effect.unsharp-mask, alpha2.effect.sharpen
vignette alpha2.effect.vignette
chromakey, colorkey alpha2.effect.chroma-key
lut3d alpha2.effect.lut-3d with a .cube effect resource
amix, volume, pan Audio tracks; a clip’s audio.gain and audio.pan
afade A clip’s audio.fade_in and audio.fade_out
loudnorm --loudness-target <LUFS> on export (EBU R128)
subtitles A caption track: see Captions from SRT
FFmpeg alpha2-host export
-ss, -to --start-ticks, --end-ticks
-r 30 --frame-rate 30/1
-c:v libx264, -c:v libvpx-vp9 --video-codec h264, --video-codec vp9
-crf, -b:v --video-quality high (a level) or --video-quality 8000000 (bits per second)
-c:a, -b:a --audio-codec aac, --audio-quality
-an --no-audio
-f mp4 --container mp4, or the output’s extension
-y --overwrite

The CLI reference lists every flag.

The release archives have a second program, alpha2, for single files. It covers the everyday ffprobe and ffmpeg jobs an editing app needs around a project:

Job FFmpeg alpha2
Inspect a file ffprobe clip.mp4 alpha2 probe clip.mp4
A poster frame ffmpeg -ss 2 -i clip.mp4 -frames:v 1 poster.png alpha2 thumbnail clip.mp4 poster.png --at 2
A representative frame ffmpeg -i clip.mp4 -vf thumbnail -frames:v 1 poster.png alpha2 thumbnail clip.mp4 poster.png
An editing proxy ffmpeg -i clip.mp4 -vf scale=iw/2:-2 proxy.mp4 alpha2 transcode clip.mp4 proxy.mp4 --proxy half
Audio to WAV ffmpeg -i clip.mp4 -ar 48000 audio.wav alpha2 export-audio clip.mp4 audio.wav
Waveform data ffmpeg ... astats alpha2 waveform clip.mp4 (JSON)

Without --at, thumbnail picks the sharpest of nine frames spread over the video that is neither black nor white. The commands that decode take --decode-policy tolerant to skip damaged input instead of failing. See CLI reference.