Toolbox runs in your browser

Index / Media

Video to GIF

Takes a few seconds out of a video and writes an animated GIF. Your browser decodes the video; the palette, the dithering and the GIF itself are built by this page. When it has finished it reads its own output back with a separate decoder and tells you whether the two agree.

The video does not leave your browser. It is decoded, drawn to a canvas and encoded again in this tab. The page has no network code, and the site's content security policy sets connect-src 'none', so it could not upload the file even if it tried. The settings below go in the URL so a link can carry them; nothing read out of the video does.
Source video

Drop a video here, or choose a file.

Nothing loaded yet.
The clip
Counted from the beginning of the video.
At most 30. A GIF stores every frame as a whole picture, so length costs more here than it does in a video.
1 to 50. Above 50 the delay between frames rounds to less than two hundredths of a second, which browsers ignore.
32 to 640. The height follows the video's aspect ratio. Never scaled up past the video's own width.
One palette for the whole clip, chosen by median cut. 256 is the most a GIF can hold.
Floyd–Steinberg. Hides banding in gradients, costs file size, and makes fewer pixels repeat between frames.
Unticked, it plays once and stops on the last frame.
Waiting for a video
--

Frames --
Size --
Frame delay --
Palette --

Result

What a GIF can and cannot do, and what this page did about it 256 colours, once. A GIF frame indexes into a palette of at most 256 entries. This page builds one palette for the whole clip by median cut — repeatedly splitting the box of colours holding the most pixels, along its longest axis, at the median — and each entry is the pixel-weighted average of the colours in its box. Colours are counted in a histogram at 6 bits per channel, and the nearest palette entry for a pixel is resolved at that same 6 bits and cached, so a colour may occasionally be matched to a neighbour of the true nearest entry. The averages themselves are exact. If the clip turns out to use fewer distinct colours than the palette has room for, the palette is those colours exactly and nothing is approximated.

Hundredths of a second. Frame delays are stored in centiseconds, so 12 frames a second is 8.33 of them. The delays alternate between the two nearest whole numbers so the clip lasts the right length of time rather than drifting, and the figure above says what the average came to. Browsers ignore a delay below 2 centiseconds and substitute 10, which is why the frame rate stops at 50.

Only what changed. After the first frame, pixels whose palette index is the same as the frame before are written as a transparent index and left showing the frame underneath, and only the rectangle containing the changed pixels is stored. That is where most of the size saving comes from, and it is why one palette slot holds no colour. Dithering defeats it: error diffusion changes almost every pixel slightly, so far fewer repeat. Frames that are identical to the one before are dropped and their delay added to it.

Downscaling halves the frame repeatedly and takes the last partial step, rather than making one large jump, which is what the image converter here does and for the same reason.

What your browser will open. The video is decoded by the browser, not by this page, so the formats that work are the ones it can already play — in practice MP4/H.264 and WebM everywhere, and MOV, HEVC or AV1 only where the browser has the decoder. There is no format this page could add without sending your file somewhere, which it will not do.

Caps. 640 pixels wide, 30 seconds, 50 frames a second, 600 frames. Anything above these is clamped and said so in a note. Sizes here use 1 KB = 1,024 bytes.