Turn an image into a video with FFmpeg
Loop a picture for a set duration and encode it as H.264. The commands were run on a 1920x1080 PNG and on five numbered frames; frame counts and sizes come from ffprobe.
Verified with FFmpeg 9.0.1 on 26 August 2026. Search intent: ffmpeg image to video.
ffmpeg -loop 1 -framerate 30 -i image.png -t 10 -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p output.mp4Repeats the image at 30 fps for 10 s: 300 frames, 1920x1080, 136 KB, because a static picture compresses to almost nothing. -pix_fmt yuv420p is what makes the file play in browsers and on phones.
Flags, explained
- -loop 1
- Input option for image files: repeat the picture indefinitely. Needs -t or -shortest to stop.
- -framerate 30
- Rate at which the looped image is read, and therefore the output frame rate.
- -t 10
- Output duration in seconds.
- -pix_fmt yuv420p
- Chroma subsampling every player supports. Without it a PNG encodes as yuv444p and many players show black.
- -framerate 1/3 -i img%03d.png
- Image sequence input: one image every 3 s, files numbered 001, 002 and so on.
- -r 30
- Output frame rate for the slideshow, duplicating frames between images.
- zoompan=z=...:d=300:s=1920x1080
- Zoom expression per frame, number of frames to generate, output size.
- -shortest
- Stop when the shortest input ends, here the 10 s of video against endless silence.
Variants
Add a silent audio track
ffmpeg -loop 1 -framerate 30 -i image.png -f lavfi -i "anullsrc=r=48000:cl=stereo" -t 10 -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p -c:a aac -b:a 64k -shortest output.mp4Some platforms reject videos without audio. anullsrc generates silence; -shortest stops it when the 10 s of video end. Output: 300 video frames plus 470 AAC frames.
Slideshow from numbered images
ffmpeg -framerate 1/3 -i img%03d.png -c:v libx264 -preset veryfast -crf 23 -r 30 -pix_fmt yuv420p slideshow.mp4Reads img001.png, img002.png and so on at one image every 3 s, then -r 30 writes a normal 30 fps file. Five images gave 15 s and 450 frames.
Ken Burns zoom with zoompan
ffmpeg -loop 1 -framerate 30 -i image.png -vf "zoompan=z='min(zoom+0.0015,1.5)':d=300:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1920x1080:fps=30" -t 10 -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p kenburns.mp4Zooms slowly into the centre over 300 frames, capped at 1.5x. Same 10 s and 300 frames, but 2.6 MB instead of 136 KB: motion costs bits.
Vertical 9:16 from a landscape image
ffmpeg -loop 1 -framerate 30 -i image.png -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" -t 6 -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p vertical.mp4Fits the picture inside 1080x1920 and pads the rest with black: 180 frames, 1080x1920.
Pitfalls we hit
- Without -pix_fmt yuv420p the output was yuv444p (74 KB for 2 s): QuickTime, Safari and most phones play it as a black screen.
- Odd image sizes: FFmpeg 9 silently rounded a 1001x751 image down to 1000x750 with yuv420p. Older releases fail with "width not divisible by 2". Add scale=trunc(iw/2)*2:trunc(ih/2)*2 to make it explicit.
- -loop 1 without -t or -shortest encodes forever.
- -framerate must come before -i; after it, it is ignored for image sequences.
- zoompan renders every frame from the full image, so it is slow on large photos; scale the source down to the output size first.
How we verified it
Run with FFmpeg 9.0.1 on 26 August 2026 against a 1920x1080 PNG. Primary command: 10.0 s, 300 frames, 1920x1080 yuv420p, 136,262 bytes. With anullsrc: 300 frames plus 470 AAC frames, 147 KB. Slideshow of five PNGs at 1/3 fps rendered at 30 fps: 15.0 s, 450 frames. zoompan: 300 frames, 2,586,674 bytes. 9:16 padding: 1080x1920, 180 frames. Without -pix_fmt: yuv444p. 1001x751 input: 1000x750 output without error.
The same edit as a typed operation
image_to_video takes a duration and an aspect ratio, fits the image into that frame with even dimensions and a player-safe H.264 pixel format, and returns the MP4 through a signed URL. The options are validated before the render starts.
{
"inputs": [{ "id": "main", "url": "https://example.com/poster.png" }],
"options": { "duration": 5, "aspect_ratio": "9:16" }
}FAQ
Why is my video black in the browser?
The PNG was encoded as yuv444p, which browsers and phones do not decode. Add -pix_fmt yuv420p to the command.
How do I give each slide a different duration?
The -framerate input option applies one duration to every image. For mixed durations use the concat demuxer with a text file that lists each file and its duration, or render each slide as a short clip and concatenate.
Can I add music instead of silence?
Yes: replace the anullsrc input with -i music.mp3 and keep -shortest so the video stops with the picture, or drop -shortest and set -t to the length of the track.
Why is the still video so small and the zoom so large?
A static frame is encoded once and then referenced, so 10 s cost 136 KB. Zooming changes every pixel every frame, so the encoder has to describe motion: 2.6 MB for the same duration.
Related commands
- ffmpeg video to gifConvert a video to GIF with FFmpegRead
- ffmpeg resize videoResize a video with FFmpegRead
- ffmpeg picture in picturePicture-in-picture with FFmpeg overlayRead