Want to make short loops from videos with FOSS command line tools? You've come to the right place!
For now it's a .zsh file and you need zsh
- Easily installed with
brewif onlinux - Install on windows
- Default on
MacOS
wget https://github.com/PeterRobots/Vid2Loop/blob/main/vid2loop.zsh
sudo chmod +x vid2loop.zshSimply pass the file you want to turn into a loop into the script.
See ./vid2loop.zsh --help for more information about commands
- whole clip
- maintain pacing
- no interpolation
- mp4 output
./vid2loop.zsh -i /path/input_file.mp4 -o /path/output_file.mp4
- Pick a start and stop time for a clip
- Speed up or slowdown clips
- Constant ratio
- First half of Sine wave peaking in the middle
- First half of Cosine wave starting slow and speeding up at end
- Interpolate frames with blend frames or motion vector calculated frames
- Output as:
- looped
.gif - looped
.avif - looped
.webp .mp4(most players enable looping) For example a slow mo style clip with varying pacing and interpolation.
- looped
0.25speed at the middle of the clip- motion vector interpolation
.avifoutput
.gifis a classic- very large in file sizes
- quality for size isn't amazing
- widely compatible
.avifa modern remake of the gif- much smaller file sizes
- good quality through av1 encoding
- compatibility is iffy
.webpalternative browser compatible focused- smaller file sizes
- decent quality
- compatibility is very good with browsers
.mp4basic widely compatible video file- much smaller file sizes
- good quality
- looping isn't baked in
- widely compatible
https://ayosec.github.io/ffmpeg-filters-docs/8.0/Filters/Multimedia/setpts.html Change the frame pacing of a video, higher is slower.
PTS = 1/($FPS*TB)N: The sequential index number of the input frame (starting at0).TB: The timebase of the input stream.PI: Mathematical constant π inside expression evaluations.T: Presentation time of the frame in seconds
https://ayosec.github.io/ffmpeg-filters-docs/8.0/Filters/Video/minterpolate.html minterpolate makes new frames for desired framerate
mi_mode=mci:mc_mode=aobmc: uses slow adv vector motion handlingmi_mode=blend: is fast and simple blendingme_mode: is the motion estimation, bilat is default, bidir is smoothervsbmc=1: sets variable block sizes As a comment,minterpolateis ok:- blend is very fast and introduces ghosting artifacts
- mci is far clearer but introduces blocking artifacts
There are far better machine learned models these days, I recommend a look. You can for example combine my other script project running the SPEED video interpolation model (2026).
- Run
vid2loop.shwith no interpolation- You can still modify the frame pacing with setpts
- Use the output file from
vid2loop.shas the input into the SPEED model script.
Encoding in av1 and for .avif was an experience.
This review helped a lot: https://catskull.net/libaom-vs-svtav1-vs-rav1e-2025.html
libaom-av1is referencelibsvtav1is open source netflix SVT-AV1librav1eis a rust open source implementation
libsvtav1 > librav1e > libaom-av1
You could use HW accelerated libraries like CUDA (nvidia) and videotoolbox (apple)
- I wanted to avoid platform dependencies so I didn't, but I do think they are decent.
Can replace ffmpeg with a progress bar wrapper like ffpb from cargo or pip.
If you like me run bazzite or some other immutable distro that includes ffmpeg but is missing things like vmaf, ignore the errors related to it.
- I tried to fix it by downloading the json
https://github.com/Netflix/vmaf/blob/master/model/vmaf_v0.6.1.jsonfrom https://github.com/Netflix/vmaf - I put it in userspace, I chose here:
/home/USERNAME/.local/share/ffmpeg/model - Add that path to
LD_LIBRARY_PATH(in.zshrcif usingzsh) - Didn't work for me and wasn't needed in the end.
Bazzite ffmpeg doesn't have vmaf, so I had to download and put inside: .local/share/ffmpeg/model/vmaf_v0.6.1.json
I'm not entirely sure why this isn't simpler, but I have to input the starting file again after encoding.
Feed it into complex filter for vmaf.
if [[ ! -z $VMAF ]]; then
SYSTEM_FLAGS=(-i "$INPUT" -filter_complex "[1:v][0:v]libvmaf=model='path=$VMAF'" -f null -)
fi