FFMPEG COMMAND

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.

the command
ffmpeg -loop 1 -framerate 30 -i image.png -t 10 -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p output.mp4

Repeats 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

shell
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.mp4

Some 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

shell
ffmpeg -framerate 1/3 -i img%03d.png -c:v libx264 -preset veryfast -crf 23 -r 30 -pix_fmt yuv420p slideshow.mp4

Reads 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

shell
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.mp4

Zooms 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

shell
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.mp4

Fits 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.

MCP tool: image_to_videoPOST /api/v1/tools/image-to-video
request body
{
  "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

All FFmpeg commands