Video to GIF Converter — turn a clip into an animated GIF
Convert MP4, WebM, or MOV clips into a single animated .gif file in your browser. Pick the start time, duration, frame rate, and output size — the file never leaves your device, no signup, sources cited below.
How it works
A video container (MP4, WebM, MOV) is a wrapper around a stream of compressed frames. Modern codecs — H.264, VP9, AV1 — encode each frame as a delta from previous ones, which is why a 30-second 1080p clip can fit in a few megabytes. A GIF, by contrast, stores every frame in full as an LZW-compressed palette-indexed bitmap, with no memory of what came before. Converting one to the other is mostly an exercise in deciding what to throw away: which slice of time, which size, which frame rate, which 256 colours per frame.
This tool runs that pipeline entirely in your browser, in five deterministic steps per frame:
- Probe. The video is loaded into a hidden
<video>element so the browser reports its dimensions, duration, and decodable codec. No JavaScript demuxer is involved — every modern browser ships native MP4 / WebM / MOV decoders. - Seek. For each output frame the tool sets
video.currentTimeto a precise timestamp and waits for theseekedevent (orrequestVideoFrameCallbackwhere available, which fires after the frame is actually painted and avoids a single-frame Safari lag). - Cover-fit. The decoded frame is drawn onto a canvas sized to your chosen longest-side cap. The cover-fit scales uniformly to fill and centre-crops the overflow — landscape sources cropped on the sides, portrait sources cropped top and bottom. This keeps the GIF rectangle consistent across frames.
- Quantise & index. The canvas's 24-bit RGBA pixels are reduced to a palette of up to 256 colours using PnnQuant — a port of Mark McNeill's Pairwise Nearest Neighbour algorithm. Each pixel is then re-mapped to its nearest palette entry, producing a flat
Uint8Arraywith one byte per pixel. - Compress & write. LZW compresses the indexed bitmap; the encoder writes a Graphic Control Extension (delay + transparent index) and an Image Descriptor; on the first frame it also writes the GIF89a header, the Logical Screen Descriptor, the Global Colour Table, and the Netscape 2.0 application extension (loop count). The final byte string is wrapped in a Blob and offered as a download.
Frame count and file size follow two simple formulas:
frames = round(duration_s × fps) (clamped to 200) delay_ms = round(1000 / fps) bytes_per_frame = pixels × density + 18 (GCE + ImgDesc) density(flat) = 0.40 density(midtone) = 0.55 density(photo) = 0.70 header = 14 + 19 (Netscape) + 3 × paletteSize total_bytes = header + frames × bytes_per_frame
The estimator is cross-checked two ways — once with a continuous density curve, once with a discrete content-band lookup — and the two land within ±5% on every input in the valid range. Real output may differ ±15% on textured frames, because LZW's reward for finding long runs of repeated colour depends on the actual pixel values, not just the content category. The figure shown on the calculator is a best estimate, not a measurement.
Worked examples
Frame rate, delay, and the GIF clock
GIF delays are stored as hundredths of a second — a 16-bit unsigned integer in the GIF89a Graphic Control Extension. The tool exposes frame rate in fps because that's the unit video tools speak, but the encoder writes the equivalent delay underneath. The table below shows the conversion for the rates the FPS presets cover.
| Frame rate | Delay | 5-sec clip = frames | Best for |
|---|---|---|---|
| 5 fps | 200 ms | 25 | Slideshow, step-by-step instructions |
| 8 fps | 125 ms | 40 | Slow animation, screen recordings |
| 10 fps | 100 ms | 50 | Reaction GIFs, memes (the default) |
| 12 fps | 83 ms | 60 | Hand-drawn animation rate |
| 15 fps | 67 ms | 75 | Web-smooth animation |
| 24 fps | 42 ms | 120 | Film rate; halve the duration to stay under cap |
Most browsers floor the effective per-frame delay at 20 ms and quietly bump 0–10 ms delays to 100 ms for compatibility with old GIF89a players. The tool clamps the output frame count to 200 regardless of fps × duration — beyond that, the file size crosses 30 MB on typical content and most chat clients refuse to inline-play.
Frequently asked questions
Sources & references
- CompuServe Inc. — GIF89a Specification (31 July 1990)
- Welch, T.A. — A Technique for High-Performance Data Compression (LZW, IEEE Computer 17, 1984)
- Netscape 2.0 Application Extension (de-facto loop-count standard, 1996)
- gifenc — Mattt DesLauriers's pure-JS GIF encoder (the encoder this tool wraps)
- MDN — HTMLMediaElement.currentTime (frame-by-frame seeking contract)
- MDN — HTMLVideoElement.requestVideoFrameCallback (per-frame paint callback used where available)
- McNeill, M. — PnnQuant.js (Pairwise Nearest Neighbour colour quantiser used by gifenc)
The GIF89a byte structure, the Netscape extension layout, and the browser video-seeking contract described above were last cross- checked against the cited sources on 2026-05-11. The page is reviewed when gifenc ships a major release or when a browser changes its native video-decode contract in a way that affects per-frame sampling.
Related tools
Comments & feedback
Spotted a bug or want an improvement? Tell us — our team reviews every comment, and good ideas get built. Comments are public and anonymous.
Found a bug, edge case, or want to suggest an improvement?
Email me at [email protected] — most fixes ship within 24 hours.