Skip to content

RESOURCES / BLOG

How to Convert Images to Video Using FFmpeg

Turning a folder full of screenshots or design comps into a clean MP4 is a common need for product walk-throughs, social posts, and quick demos. The community often runs into the same hurdles: file ordering, per-slide durations, audio syncing, and best encoding settings for compatibility. 

Hi all,
What is the simplest, repeatable approach for how to convert images to video using FFmpeg?
I have a sequence of images and need to create a video with a specific frame rate, make sure files are in the right order even if names are not numbered, optionally add audio, and keep the output compatible with most players and social platforms.
Any tips on quality settings, handling different image sizes, and setting per-image durations would be great. Thanks!

This job is best done with FFmpeg, due to its image input, timing, and encoding abilities. Let’s cover the most common workflows, from quick numbered-sequence renders to advanced per-image durations and audio.

If your files are named consistently (for example frames/0001.jpg, 0002.jpg…), this produces a 30 fps 1080p H.264 video that plays everywhere:

ffmpeg -framerate 30 -i "frames/%04d.jpg" \
  -vf "scale=1920:-2,format=yuv420p,fps=30" \
  -c:v libx264 -preset medium -crf 20 -pix_fmt yuv420p \
  -movflags +faststart output.mp4Code language: JavaScript (javascript)
  • -framerate sets the input frame cadence. -vf fps=30 ensures a constant output frame rate.
  • scale=1920:-2 preserves aspect ratio and snaps height to a multiple of 2.
  • libx264 with -crf 18 to 23 is a good quality range. Lower CRF means higher quality and larger files.
  • -movflags +faststart optimizes MP4s for web playback.

For arbitrary filenames and per-image durations, use the concat demuxer. Create a text file:

# images.txt
file '01.png'
duration 3
file 'slide-b.jpg'
duration 5
file 'final.png'
duration 2
# repeat last file without duration so the final duration takes effect:
file 'final.png'Code language: PHP (php)

Then run:

ffmpeg -f concat -safe 0 -i images.txt \
  -vsync vfr -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
  -movflags +faststart slideshow.mp4Code language: CSS (css)
  • -safe 0 allows absolute paths in images.txt if needed.
  • -vsync vfr keeps variable frame rate where durations differ.
ffmpeg -f concat -safe 0 -i images.txt -i music.mp3 \
  -shortest -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
  -c:a aac -b:a 192k -movflags +faststart slideshow_with_audio.mp4Code language: CSS (css)
  • -shortest ends the render when either the images or audio finishes.
  • -c:a aac is widely compatible for MP4 audio.

To fit everything into 1920×1080 while preserving aspect ratio:

-vf "scale=1920:1080:force_original_aspect_ratio=decrease,\
pad=1920:1080:(ow-iw)/2:(oh-ih)/2,format=yuv420p,fps=30"Code language: JavaScript (javascript)
  • Number your files consistently (0001, 0002…) and use -start_number if needed.
  • For simple alphabetic ordering: -pattern_type glob -i "*.jpg" then encode as above.
  • H.264 is the safest default for broad compatibility.
  • FFmpeg offers many features beyond the basics.

You can animate still images with pan and zoom effects for more visual interest. If you like the Ken Burns style, this primer helps you plan tasteful motion: Ken Burns effect guide.

After rendering, you can upload and deliver the final MP4 efficiently with Cloudinary. This is especially helpful when you manage lots of media assets across web and mobile.

Example upload with the Node SDK:

import { v2 as cloudinary } from "cloudinary";

cloudinary.config({
  cloud_name: "YOUR_CLOUD",
  api_key: "YOUR_KEY",
  api_secret: "YOUR_SECRET"
});

await cloudinary.uploader.upload("slideshow.mp4", {
  resource_type: "video",
  folder: "slideshows",
  public_id: "demo_show"
});
// Delivery (auto quality and format):
// https://res.cloudinary.com/YOUR_CLOUD/video/upload/q_auto,f_auto/slideshows/demo_show.mp4Code language: JavaScript (javascript)
  • Upload once, deliver globally with optimized formats and bitrates.
  • Offload transformations and reduce your server work while keeping a simple build step with FFmpeg.
  • Green or washed-out playback: ensure -pix_fmt yuv420p.
  • Jumpy timing: use the concat demuxer with explicit durations, not just -framerate, if slides vary in length.
  • Large files: increase CRF slightly or choose a slower preset for better compression.
  • For numbered files: use -framerate on input, scale, libx264, CRF 18 to 23, yuv420p, and +faststart.
  • For custom timings: create a concat file with durations and use -vsync vfr.
  • Add audio: -i music.mp3 and -shortest.
  • Stick to H.264: It offers better compatibility and follows solid encoding practices.

Ready to optimize creation and delivery of your videos at scale? Sign up for a free Cloudinary account and streamline your workflow from upload to global delivery.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free