# Make a GIF from a video with FFmpeg — and when you need no video at all

> Measured with FFmpeg 9.0.2: the one-liner that made a 38 MB GIF, the palette command that fixes quality, why MP4 is smaller, and GIFs from HTML.

Published 2026-11-07 · https://shareglint.com/en/guides/gif-from-html-instead-of-ffmpeg

FFmpeg is the standard tool for turning a video into a GIF, and its one-line conversion is also the reason so many GIFs are huge and blotchy. This page measures what each step does to file size and quality, gives the command that works, and ends with the case where you do not have a video at all — an animation built in HTML and CSS — which needs no FFmpeg.

Every number here was measured on 2026-11-07 with **FFmpeg 9.0.2** (the `linuxserver/ffmpeg` Docker image), on a deliberately hard test clip: 6 seconds of 1280×720 at 30 fps, FFmpeg's `testsrc2` pattern blended with a moving Mandelbrot set — lots of colour, detail and motion. Quality is SSIM against the source scaled the same way: 1.0 is identical, lower is worse.

## What we measured

| Command (on the 6-second clip) | Output | Size | SSIM |
|---|---|---|---|
| `ffmpeg -i in.mp4 out.gif` | 1280×720, 180 frames | **37.9 MB** | — |
| 3 s, `fps=15`, `scale=480:-1` | 480×270, 45 frames | 1.74 MB | **0.49** |
| same + `palettegen` / `paletteuse` | 480×270, 45 frames | 2.83 MB | 0.87 |
| same + `stats_mode=diff`, Bayer dithering | 480×270, 45 frames | 2.02 MB | 0.90 |
| same clip as MP4 (H.264, CRF 23) | 480×270 | **0.16 MB** | 0.95 |

The one-liner made a 38 MB GIF from 6 seconds of video. Cutting length, frame rate and size took it to 1.7 MB — but without a palette, FFmpeg's GIF encoder maps every frame to a fixed set of colours, and the result scored 0.49: visibly banded and noisy. A palette made from the clip itself cost a bit more space and lifted the score to 0.87.

## The command that works

```bash
ffmpeg -ss 1 -t 3 -i in.mp4 \
  -vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
  out.gif
```

Read it left to right:

- **`-ss 1 -t 3`** — start at 1 second, keep 3 seconds. Placed before `-i`, FFmpeg seeks in the input instead of decoding everything before it. Length is the biggest lever: size grows with every frame.
- **`fps=15`** — 15 frames per second is smooth for most motion; GIFs at 30 fps double the frames for little visible gain.
- **`scale=480:-1:flags=lanczos`** — 480 pixels wide, height in proportion, with a sharp scaler. GIFs are shown small in chats and emails; 1280 pixels is wasted.
- **`split … palettegen … paletteuse`** — GIF allows at most 256 colours. [`palettegen`](https://ffmpeg.org/ffmpeg-filters.html#palettegen) picks the 256 that best fit this clip, and `paletteuse` maps the frames onto them, in one pass.

Looping is the default: FFmpeg's GIF muxer documents `-loop` as "0 for looping indefinitely (default)", and `-loop -1` plays once ([FFmpeg formats](https://ffmpeg.org/ffmpeg-formats.html)).

## Tuning the palette

Two options moved our numbers further:

- **`palettegen=stats_mode=diff`** builds the palette from the parts that change between frames. FFmpeg's docs describe it as giving "more importance to the moving part of your input if the background is static" — screen recordings, UI demos, a logo animating on a flat background.
- **`paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle`** uses an ordered dither (a regular pattern compresses better than random noise) and redraws only the changed rectangle of each frame.

Together they made the GIF smaller (2.02 MB against 2.83 MB) and slightly closer to the source (0.90 against 0.87). For flat graphics — text, icons, solid colours — try `dither=none` too and compare: flat colours need no dithering.

## When MP4 is the better answer

The same 3-second, 480-pixel clip was **160 KB as MP4** — about 13 times smaller than the best GIF — and the most faithful of all (0.95):

```bash
ffmpeg -ss 1 -t 3 -i in.mp4 -vf "fps=15,scale=480:-2:flags=lanczos" \
  -c:v libx264 -crf 23 -pix_fmt yuv420p -movflags +faststart -an out.mp4
```

`scale=480:-2` keeps the height even, which H.264 requires; `-pix_fmt yuv420p` makes the file play everywhere; `-movflags +faststart` lets it start before it has fully downloaded; `-an` drops the audio. On a web page, a muted, looping `<video autoplay muted loop playsinline>` behaves like a GIF. Social networks, Discord and Telegram all take MP4.

GIF still wins where video is not accepted: **email**, where video generally does not play and GIF is the usual way to animate, some forums and older chat tools, and places that only take an image upload.

## When there is no video: GIFs from HTML

Many GIFs are not cut from footage at all. A countdown to a launch, an animated stat ("10,000 users"), a product reveal, a "typing" headline — these are designed as HTML and CSS animations, then screen-recorded and converted with FFmpeg. That round trip loses quality twice and needs a browser, a recorder and the commands above.

Shareglint renders a template's **CSS animation straight to a GIF or MP4**: the animation is drawn frame by frame by Chrome and encoded once, with the template's data filled in, so the same countdown can show a different date for every customer.

```bash
curl -X POST "https://shareglint.com/v1/images?wait=true" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "template": "animated-countdown", "format": "gif",
        "modifications": { "event": "Doors open", "number": "3", "unit": "days" } }'
```

Limits, honestly: animations up to 5 seconds, a long edge of at most 1080 pixels, and 5 renders of quota per animated file. It records CSS animations of HTML templates — it does **not** take a video as input, and it does not trim, convert or edit videos; for that, FFmpeg as above is the right tool. [HTML to GIF](https://shareglint.com/en/html-to-gif) shows which templates animate and how to write your own.

## Checklist for a GIF from video

1. Keep it short: 2–4 seconds of the part that matters.
2. 10–15 fps and 320–600 pixels wide.
3. Always use a generated palette; try `stats_mode=diff` for static backgrounds.
4. Check the file size before sending: a GIF over a few megabytes loads slowly in email and chat.
5. If the destination plays video, send MP4 instead.

## FAQ

### How do I make a good GIF from a video with FFmpeg?

Trim, lower the frame rate and size, and generate a palette from the clip itself: ffmpeg -ss 1 -t 3 -i in.mp4 -vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" out.gif. In our test the palette raised similarity to the source (SSIM) from 0.49 to 0.87.

### Why is my FFmpeg GIF so big?

GIF stores every frame with lossless compression and at most 256 colours. Converting a 6-second 1280×720, 30 fps clip with plain ffmpeg -i in.mp4 out.gif gave a 37.9 MB file in our test. Cut the length, the frame rate and the width first; they matter far more than any encoder option.

### Is MP4 better than GIF?

Wherever video is accepted, yes: the same 3-second, 480-pixel clip was 160 KB as MP4 and 2.0–2.8 MB as a palette GIF, and the MP4 was closer to the source. Use GIF where only images work, above all in email.

### Can I make a GIF from HTML and CSS without recording a video?

Yes. Our API renders an HTML template's CSS animation straight to GIF or MP4, so there is no screen recording and no FFmpeg step. It records animations of up to 5 seconds; it does not edit or convert existing videos.
