Video tools

Video thumbnail

Grab a single frame from a video as a PNG or JPEG, in the browser only, like pdf-to-image (src/tools/pdf-to-image.ts), because decoding a frame needs WebCodecs, which Node has no equivalent of. Nothing is uploaded.

Runs on your device. The file is never uploaded.

Video thumbnail decodes a single frame of your video at the atSec second, 0 by default, and saves it as PNG or JPEG. PNG is the default, and a JPEG comes back with a .jpg name rather than .jpeg. A frame cannot be decoded without WebCodecs, which Node lacks, so this one works on the page alone.

Input

Options

Questions

How do I pick which frame I get?

With the atSec option, a time in seconds, which defaults to 0 for the first frame. The tool decodes the frame at or near that time and reports the exact timestamp it landed on, for example "frame at 12.480s", so you know what you got. A negative value is refused with "atSec must be a non-negative number".

What image formats can it save?

PNG or JPEG, chosen with the format option, PNG by default. PNG files are named after your video with .png on the end, JPEG files with .jpg. The frame is written at the native size of the video, since there is no resize option here. Use Resize / crop video first, or an image tool afterwards, if you need a smaller picture.

Why does it need a browser?

Because decoding a frame needs WebCodecs, and Node has no equivalent. The tool is marked browser only, so the command line and MCP surfaces refuse it with "video-thumbnail needs a browser (OffscreenCanvas). Use it at /video/video-thumbnail instead." On that page the decode happens in a Web Worker in your tab.

Why did I get a codec error?

Because your browser cannot decode that video codec. The tool asks the browser first and refuses with "this browser cannot decode its video codec" rather than failing halfway. It reads H.264, H.265, VP8, VP9 and AV1, and which of those your particular browser supports varies by platform. A file with no video track at all is refused with "no video track found".

Why did it say my timestamp is before the first frame?

Because no frame exists at or before the time you asked for. Some files start their timeline at a value above zero, so a small atSec can land before anything decodable. The message says that atSec is before the first frame of the video. Nudge the value up by a fraction of a second and try again.

Is the video uploaded to get the frame?

No. The whole file is read into memory in your tab and decoded there. Nothing is sent to a server. It does mean the entire video sits in browser memory even though you only wanted one picture out of it, so a large source file can be limited by your device.

Related Video tools

n→nExtract video framesPull out one frame every N seconds as a sequence of PNGs, in the browser only, like pdf-to-image (src/tools/pdf-to-image.ts), because decoding frames needs WebCodecs, which Node has no equivalent of.Runs on each file you dropn→nVideo to GIFConvert a video clip to an animated GIF. In the browser only, like video-extract-frames (src/tools/video-extract-frames.ts), because decoding frames needs WebCodecs, which Node has no equivalent of. WebCodecs has no GIF codec at all, so this decodes frames with mediabunny/WebCodecs and then builds the GIF89a bytes itself (src/gif.ts): one shared 256-colour palette (median-cut, sampled across every frame this tool decodes, not per-frame) so colours stay consistent frame to frame, with each pixel mapped to its nearest palette entry. No dithering.Runs on each file you dropn→nGIF to videoConvert an animated GIF into a video, in the browser only, like video-convert (src/tools/video-convert.ts), because encoding needs WebCodecs, which Node has no equivalent of. WebCodecs has no GIF codec at all, so this parses the GIF89a bytes itself (src/gif.ts), including disposal method and transparency compositing, so a GIF built from partial-frame deltas decodes to the same full frames a browser would show, not only its raw per-frame rectangles. It then feeds the composited frames to mediabunny/WebCodecs to encode. Always outputs MP4, following every other Node-native remux tool in this codebase; each frame's own GIF delay becomes its video frame duration. MP4 has no alpha channel, so any transparent GIF pixel, composited onto a canvas that starts fully transparent black, becomes opaque black in the video, not the checkerboard a GIF viewer shows; verified by executing this tool against a transparent fixture and reading the pixel back out.Runs on each file you drop