Drag & drop MP4, WebM, or MOV — or click to choose
One file · up to 250.00 MB · client-side only (GIF-008)
About this tool
Video is the default language of product demos, social proof, lectures, and gameplay—yet many downstream workflows still demand **still images**: a sharp thumbnail for YouTube, a reference plate for a storyboard, a PNG proof for a bug report, or a folder of frames for computer-vision labeling. Desktop editors can absolutely export stills, but they often mean installing codecs, fighting project settings, or uploading sensitive footage to a third-party “free converter” that you cannot clear through IT. SynthQuery’s **Video Frame Extractor** keeps the entire pipeline **inside your browser tab**: you load an **MP4**, **WebM**, or **MOV** from disk, scrub with a dedicated timeline, step forward and backward using an **assumed frame rate** you control, capture the exact moment on screen as **PNG** or **JPEG**, and—when you need coverage instead of one hero still—run **interval extraction** every one, five, ten, or thirty seconds, or at a **custom** cadence you type. Every grab uses the HTML5 **video** element paired with an offscreen **canvas** draw and the canvas **toBlob** API, so pixels never traverse SynthQuery’s application servers as part of the conversion. A **thumbnail-style timeline strip** shows where you have already extracted frames (marker dots) plus a live playhead tied to the scrubber, which makes long interviews and tutorials easier to navigate without exporting the whole file first. When batch results pile up, download each still individually from the list or bundle everything into a **ZIP** archive built with JSZip, again entirely client-side. The tool is tuned for **mobile-responsive** layouts: controls stack, touch targets stay large, and heavy work only begins after you intentionally press extract—so first paint stays friendly to Core Web Vitals on real devices, not just synthetic lab tests.
What this tool does
The interface is organized around three cooperating ideas: **precision navigation**, **repeatable sampling**, and **audit-friendly exports**. Navigation combines standard HTMLMediaElement timing with deterministic **ensureVideoTime** seeks: when the requested timestamp already matches within about half a frame at sixty fps, the tool skips redundant seeks because some engines omit the **seeked** event when the decoder believes nothing changed—this prevents twenty-second timeouts on the first frame of short clips. The **assumed FPS** control is honest about browser limitations: without demuxing bitstreams in JavaScript, we cannot always know GOP structure or true CFR cadence, but creators usually remember whether they recorded at thirty or sixty, and matching that number yields predictable nudges for storyboard work.
**PNG versus JPEG** follows the same philosophy as other SynthQuery imaging utilities. PNG uses the canvas **image/png** blob path and preserves each pixel sampled from the decoded frame—ideal for overlays, UI captures, and any still you might composite later. JPEG uses **image/jpeg** with a bounded quality parameter; it is smaller for email and CMS thumbnails but cannot store partial transparency (most camera-derived clips are fully opaque anyway). **Interval extraction** builds its timestamp list with the **buildIntervalTimestamps** helper, walking from zero through duration in even steps and appending the true end time when the last sample would otherwise miss the closing frame—helpful for clips whose duration is not an integer multiple of your cadence.
The **timeline strip** composites a **low-quality JPEG thumbnail** of the first decodable frame (or whichever frame loaded after metadata) as a darkened background, then paints **primary-colored dots** at proportional positions for each extracted timestamp and a high-contrast **playhead** synchronized to the scrubber and **timeupdate** events. This satisfies the “thumbnail timeline with frame markers” requirement without spawning a second decode pipeline for every pixel of the movie. **ZIP packaging** loads JSZip lazily on first use so the initial route bundle stays smaller; each member file name embeds a human-readable timecode string plus a sequence counter to avoid collisions when duplicate seeks land on the same second due to floating-point rounding.
Technical details
SynthQuery uses the browser’s built-in **demuxer and decoder** by assigning the video element **src** to an object URL created from your **File** object. Frame grabs call **drawImage** on a canvas sized to the **intrinsic frame dimensions** reported by the element—**not** the CSS box—so exports stay at full resolution even when the preview is letterboxed on phones. Seeking relies on the **seeked** event with a timeout guard; **ensureVideoTime** avoids redundant seeks when **currentTime** already matches within a small epsilon. Timestamp math rounds to milliseconds in a few helpers to keep filenames stable across engines.
**Canvas toBlob** chooses MIME types **image/png** or **image/jpeg**; JPEG quality maps to the second argument where supported. Extremely wide or tall frames are rejected if either edge exceeds the shared **maximum canvas edge** constant used across SynthQuery (sixteen thousand three hundred eighty-four pixels), matching PNG→JPEG tooling elsewhere in the repo so support teams can cite one limit table. **Memory** scales with the number of blobs retained in the extracted list; clearing rows revokes their object URLs, and removing the video revokes the media object URL. **CORS** is not a factor for local files because the blob URL is same-origin to the page. **Audio tracks** are ignored—this is a stills workflow—though the video element may still decode audio internally depending on the browser; muting is not strictly required for canvas paints but keeps autoplay policies predictable if you press play.
Use cases
**YouTube and podcast editors** often need three candidates for a custom thumbnail: the moment the guest smiles, the slide transition mid-sentence, and a safe wide shot. Stepping frame-accurately enough for social, then exporting PNG, replaces screenshot hacks that grab player chrome or HDR overlays by accident.
**Storyboard artists and game cinematic directors** can pepper a timeline with interval grabs every five seconds, drop the ZIP into a board tool, and discard weak panels—faster than batch exporting from a non-linear editor for early reviews.
**Computer vision and ML ops teams** sometimes want sparse stills for **human review** before committing to full-frame extraction on a GPU cluster; interval mode offers a lightweight sanity check that lighting and subject matter are usable.
**Teachers and workshop facilitators** freeze equations or IDE highlights from screencasts for printable handouts; JPEG keeps Moodle uploads under quota while PNG preserves thin syntax highlighting.
**Film students doing shot analysis** step through clips, saving reference frames when composition or eyeline peaks, then annotate in a separate document—no license server calls required during crunch weeks.
**Support engineers** reproduce UI glitches from customer MP4 repros: capture the exact glitch frame as PNG, redact in an image editor, and attach to Jira without re-encoding the whole video.
**Accessibility reviewers** pair still extraction with their normal checklist: confirm captions, on-screen text legibility, and color contrast on representative frames pulled at known timestamps.
How to use this tool effectively
Start with a clip you are legally and ethically allowed to process; local execution does not waive copyright, likeness, or workplace media rules. Visit **synthquery.com/video-frame-extractor** in a current version of Chrome, Edge, Firefox, or Safari—WebCodecs are not required here, but a modern **video** implementation with reliable **seeked** events produces the steadiest results. Tap the dashed upload card or drag a file onto it; accepted containers include **MP4** (typically H.264 or H.265 inside), **WebM** (often VP8/VP9/AV1), and **MOV** (QuickTime-style). The picker filters on both extensions and MIME hints, and the hard limit is **two hundred fifty megabytes** per file to keep laptop RAM predictable when multiple full-resolution bitmaps sit in memory at once.
After metadata loads, the **preview** pane shows your video with custom transport controls: **Play/Pause**, **previous frame**, **next frame**, current **timecode** versus total duration, and a **slider** that maps linearly across the whole timeline. Frame stepping does not read proprietary codec frame indexes (browsers rarely expose them uniformly); instead you set an **assumed FPS** field (defaults to thirty) and each step moves time by **one divided by that FPS**. If you know the source is twenty-four fps cinema or sixty fps gameplay, type that number so each click tracks real editorial frames more closely—when in doubt, leave thirty and rely on the scrubber for exact composition. When the picture looks right, pick **PNG** for lossless stills or **JPEG** with the **quality** slider for smaller attachments. Press **Download current frame** to save immediately **and** append a thumbnail row to the **Extracted frames** list so you can ZIP everything later without hunting your Downloads folder.
For coverage extraction, open the **Interval extraction** card. Buttons set **every 1s, 5s, 10s, or 30s**, while **Custom** unlocks a numeric field (minimum **0.1** seconds, maximum **3600**). Press **Extract at interval** to seek sequentially, capture after each **seeked** event, and push results into the same list. The tool refuses runs that would exceed **five hundred** frames per pass—if you hit that guard, widen the interval or trim the clip in another editor first. Finish by downloading **all as ZIP** or removing individual rows with the trash icon; use **Remove** under the filename to unload the video entirely and revoke object URLs, which matters on shared computers.
Pull composited frames from animated GIFs with disposal-aware thumbnails, delays, PNG/JPEG export, and ZIP—ideal when motion already lives in GIF89a instead of MP4 (GIF-007).
When motion arrives as animated WebP rather than GIF or MP4, convert into a classic GIF container for places that still reject modern codecs—pair with this extractor when you need stills from the result.
After exporting JPEG frames here, merge them into a print-ready PDF for decks, tickets, or archival packets without leaving the browser.
Frequently asked questions
The picker accepts **MP4**, **WebM**, **MOV**, and common **MKV** variants by extension and MIME hint. Actual decode success still depends on what your browser ships—**H.264** in MP4 is the safest cross-browser choice, **VP9** WebM works well in Chromium and Firefox, and **HEVC** inside MOV may fail on operating systems that withhold the codec from web engines. If playback works in the preview, extraction will work; if the preview never leaves a black frame, re-encode locally with FFmpeg or HandBrake before returning here.
No. The File API reads bytes into memory, **URL.createObjectURL** feeds the media element, canvas reads pixels on your machine, and JSZip—only when you request a ZIP—also runs entirely in-tab. Network traffic is limited to loading the SynthQuery web application assets (HTML, JavaScript, CSS) like any normal page visit. IT teams still see domain DNS lookups and TLS handshakes in their logs, but the **media payload** is not sent to SynthQuery as part of this tool’s conversion workflow.
Browsers do not expose a consistent, codec-agnostic **frame index** API for arbitrary MP4 files. Some builds offer **requestVideoFrameCallback**, but support and semantics vary when the file uses B-frames or variable frame rate. SynthQuery therefore steps **currentTime** by **one divided by the FPS you enter** (default thirty). Match your shoot settings—twenty-four, twenty-five, thirty, sixty—for intuitive “next frame” behavior, and use the scrubber when you need arbitrary sub-frame alignment that steps cannot hit.
Each sample waits for the **seeked** event after assigning **video.currentTime** to a calculated timestamp, then captures immediately. That matches how most browser editors sample stills and is accurate to the decoder’s output for that seek point. Floating-point duration and keyframe placement mean two adjacent runs might differ by a few milliseconds from desktop FFmpeg with **-ss** before **-i**; for legal forensics or broadcast QC, prefer deterministic CLI tooling. For thumbnails, storyboards, and ML spot checks, this approach is typically sufficient.
Individual uploads are capped at **two hundred fifty megabytes** to protect RAM on mid-tier laptops. Interval mode refuses to queue more than **five hundred** frames per run; you will see a toast explaining the cap. Extract as many single frames as you need manually—the practical limit is patience and memory. If you routinely exceed these bounds, trim clips offline or raise the interval.
Stills reflect whatever the decoder drew for that timestamp—they cannot exceed source quality, and recompression matters when you choose **JPEG** because it is lossy. **PNG** preserves the decoded RGB bitmap exactly (subject to canvas alpha premultiplication rules, which rarely affect opaque camera footage). Heavy **compression artifacts** from a low-bitrate source will still appear in the PNG; the tool does not denoise or upscale.
The canvas path operates in the browser’s compositing space, which may **tone-map or clip** HDR content differently than a professional color-managed player. For marketing stills this is often acceptable; for **color-critical cinema** work, export from a graded master in DaVinci Resolve or similar. If colors look washed or neon compared to VLC, assume a transfer-function mismatch rather than a SynthQuery bug.
ZIP creation compresses many in-memory blobs on the main thread (or worker pool depending on JSZip settings—here default async generation). Hundreds of full-HD JPEGs mean hundreds of megabytes of compression work. Close other heavy tabs, prefer **JPEG** over PNG for large batches, or split the job into multiple interval passes. The UI shows a spinner on the ZIP button while **generateAsync** runs.
Yes—the layout stacks vertically, buttons use shadcn sizing suitable for touch, and the video element sets the **playsInline** attribute to avoid iOS fullscreen takeovers during quick previews. Very large files may still exhaust mobile RAM; prefer shorter clips or coarser intervals. Safari’s codec support differs from Chrome; if a file plays on desktop Safari but not mobile, re-wrap or re-encode.
**GIF Frame Extractor** targets **GIF89a** animations with disposal-aware compositing via gifuct-js—perfect for memes and sticker pipelines. **Video Frame Extractor** targets **MP4/WebM/MOV** via the media element—ideal for camera footage and screen recordings. Use GIF tools when the asset is already a GIF; use this page when the asset is a packaged video file. Both stay client-side and share ZIP ergonomics.