The task, and why it is fiddlier than it looks
"Give me one frame per second from this clip" is one of the most common video chores there is. A QA tester needs evenly spaced screenshots to attach to a bug report. A researcher needs a sampled image sequence to feed into an annotation tool. A social editor needs twenty candidate stills from a two-minute cut without scrubbing the timeline by hand.
The task itself is simple. What makes it fiddly is that "one frame per second" is ambiguous the moment you look closely. Do you mean one frame for every second of playback time, or every Nth frame of the encoded stream? Those are different things: a 24 fps clip and a 60 fps clip produce the same count under the first reading and wildly different counts under the second. Pick the wrong one and you either get a folder of near-duplicates or a sequence too sparse to be useful.
Both approaches below produce identical pixels. They differ in how you express what you want, and in how quickly you find out you expressed it wrong.
Method 1: FFmpeg, parameter by parameter
The canonical command is short:
ffmpeg -i input.mp4 -vf fps=1 -q:v 2 frame_%04d.jpg
Every piece of it matters:
-i input.mp4— the source file. FFmpeg reads it once, start to finish; it does not seek to each timestamp individually.-vf fps=1— the video filter that does the actual work.fps=1means one output frame per second of playback time, regardless of the source frame rate. Usefps=1/5for one frame every five seconds, orfps=2for two per second. This is the playback-time reading.-q:v 2— JPEG quality, on an inverted scale where 2 is near-lossless and 31 is heavily compressed. Omit it and you get FFmpeg's default, which is noticeably softer than most people expect. If you want lossless output, writeframe_%04d.pnginstead and drop this flag entirely.frame_%04d.jpg— the output pattern.%04dzero-pads the counter to four digits:frame_0001.jpg,frame_0002.jpg. The padding is not cosmetic; see the numbering problem below.
If you want the other reading — every Nth encoded frame rather than every N seconds — the filter changes:
ffmpeg -i input.mp4 -vf "select=not(mod(n\,10))" -vsync vfr frame_%04d.png
This keeps frames where the frame index n is divisible by 10, and -vsync vfr stops FFmpeg from duplicating frames to pad out a constant output rate. Leave that flag off and you will get far more files than you asked for, which is a common and confusing first result.
To limit the work to a segment, put -ss before -i so FFmpeg seeks before decoding rather than decoding and discarding:
ffmpeg -ss 00:01:30 -to 00:02:15 -i input.mp4 -vf fps=1 frame_%04d.png
Method 2: The same job in a browser
The frame extractor on this site maps to the same two readings, with the ambiguity made explicit in the interface instead of hidden in a filter string.
- Drop the file in. It is read straight from disk into browser memory — there is no upload, so a 3 GB source starts processing immediately rather than after a transfer.
- Pick the extraction method. "Time Interval" is the equivalent of
fps=1/N— you enter the gap in seconds. "Frame Rate (FPS)" is the equivalent of theselect/modfilter — you enter N to keep every Nth frame. - Read the frame-count hint before extracting. This is the part that saves the most time. The interface shows how many images your current settings will produce before anything runs, so the 4,000-file mistake becomes a number you see rather than a folder you discover.
- Set format and quality. PNG for lossless, JPG or WebP with the 10–100% slider when size matters. The slider position corresponds to what
-q:vcontrols, expressed the way most people think about it. - Trim the range if needed. The dual-handle slider is the
-ss/-toequivalent, with the timestamps shown as you drag. - Export. Frames appear in a preview grid; click one to save it alone, or take the whole set as a single ZIP.
Rule of thumb for choosing an interval: for a talking-head or screen recording, 1 frame every 2–5 seconds captures every meaningful state change. For sport or anything with fast motion, 2–5 frames per second. Reserve "every frame" for genuine frame-accurate analysis — a 60 fps minute is 3,600 images and several gigabytes of PNG.
Three problems that actually cost people time
1. The output is thousands of near-identical frames
Almost always a units mix-up: you meant "every 10th frame" and wrote fps=10, which means ten frames per second — on a 30 fps source that is 3× more images than the naive expectation, not 3× fewer.
Before running anything, do the arithmetic. Playback-time mode: duration ÷ interval. Frame-index mode: duration × source_fps ÷ N. A 10-minute 30 fps clip at "every 10th frame" is 600 × 30 ÷ 10 = 1,800 images. If that number surprises you, adjust before you start rather than after. In the browser tool the same figure appears under the interval field automatically.
2. Extraction stalls partway through a long video
Different causes on each side, so the fix differs too.
With FFmpeg, a stall is usually disk-related — thousands of PNGs land in one directory and the filesystem, not the decoder, becomes the bottleneck. Write to a fresh empty directory, prefer JPG when you do not need lossless, and split very long sources into segments with -ss/-to.
In the browser, the limit is RAM: every extracted frame is held in memory until you download the ZIP. A few hundred 4K PNGs is several gigabytes and a tab can be killed for it. The reliable pattern is to work in chunks — use the time-range slider to take 0:00–2:00, download that ZIP, then move the range to 2:00–4:00. Closing other tabs before a large job genuinely helps, and JPG at 85–90% quality cuts memory pressure several-fold with no visible difference for most uses.
3. Frame numbering breaks the import order
This one bites after the export looks fine. Tools that sort filenames as text — and most do — order frame_10.png before frame_2.png, because "1" sorts before "2" character by character. Your sequence plays back scrambled and it is not obvious why.
The fix is zero-padding, wide enough for the largest count you will ever produce. That is what %04d is for; use %05d if you might exceed 9,999 frames. The browser tool pads automatically, which is the main reason its output tends to drop straight into an image-sequence importer without renaming.
When FFmpeg is still the right call
Plenty of the time. This is not a case where the browser wins across the board:
- Anything scripted or scheduled. A cron job, a CI step, a server-side pipeline — a browser cannot participate in those at all.
- Batches across many files. A shell loop over 200 clips is trivial in FFmpeg and tedious anywhere else.
- Codecs the browser will not decode. ProRes, DNxHD, raw camera formats, unusual containers. If the browser cannot play it, it cannot extract from it. FFmpeg handles nearly everything.
- Filtering during extraction. Deinterlacing, scaling, colour-space conversion, cropping, scene-change detection — all available as filters in the same pass.
- Reproducibility. A command in a README is exact. "Set the slider to about here" is not.
The browser tool is the better fit for one-off jobs, for anyone who does not want to install and maintain a toolchain for an occasional task, when the source is confidential enough that local-only processing matters, and when you want to look at the frames before committing to the export. Those are genuinely different situations, not a ranking.
Quick reference
| What you want | FFmpeg | Browser tool |
|---|---|---|
| One frame per second | -vf fps=1 |
Time Interval = 1 |
| One frame every 5 seconds | -vf fps=1/5 |
Time Interval = 5 |
| Every 10th encoded frame | -vf "select=not(mod(n\,10))" -vsync vfr |
Frame Rate mode, N = 10 |
| Only 1:30 to 2:15 | -ss 00:01:30 -to 00:02:15 |
Time-range slider |
| Lossless output | .png output pattern |
Format = PNG |
| Smaller files | -q:v 5 with .jpg |
JPG + quality slider |
| See the count before running | Calculate manually | Shown under the interval field |
| Preview before saving | Open the output folder | Built-in preview grid |
| Automation / batch | Shell loop, cron, CI | Not possible |
| ProRes, DNxHD, raw | Supported | Only what the browser decodes |
Whichever route you take, the interval decision comes first and the tool second. Work out how many frames you actually need, confirm the count before you start, and the rest is mechanical.