Making a good GIF from a video: frame rate, size, colours and when GIF is the wrong answer
A clip that weighs 400 KB as an MP4 comes out of a GIF converter at 12 MB, and the chat window you wanted to paste it into refuses it. That is not a bug in the converter. GIF is a 1989 image format that was never designed to carry video, and every decision baked into it — one palette of at most 256 colours per frame, lossless LZW compression, timing in hundredths of a second — works against a modern 30 fps, 1080p recording. By the end of this guide you will know why the file balloons, the three settings that shrink it by an order of magnitude, what the GIF, trim and resize tools on this site do and do not control, and the point at which to stop making a GIF and ship a small MP4 or WebP instead.
Why a GIF is so much bigger than the video it came from
A GIF89a file stores each frame as indexedpixels — one byte per pixel, pointing into a colour table of at most 256 entries — and packs those bytes with LZW, a lossless dictionary compressor, so camera noise and every step of a gradient are stored exactly. H.264 does the opposite twice over: it discards detail below what you can see, and it describes most frames as “the previous frame with these blocks moved and this small correction” — motion compensation. GIF’s only inter-frame trick is that a frame may be smaller than the canvas and may mark pixels transparent so the previous frame shows through; ffmpeg’s GIF encoder does this by default (its offsetting and transdiff flags). That is a big win when only a cursor moves and nothing at all when every pixel changes every frame.
The arithmetic is unforgiving. A 1280×720 frame is 921,600 pixels; at 30 fps, ten seconds is 300 frames; that is 276,480,000 index bytes before LZW gets to work. The same ten seconds as H.264 at 1 Mbit/s is 1,250,000 bytes. LZW and frame differencing take a large bite out of a flat-colour UI capture and a small bite out of camera footage, but no setting turns 276 million bytes into a 2 MB file. The MP4 gets there by never storing most of those bytes at all.
Timing is the other 1989 limit. A frame delay is stored in hundredths of a second, so 30 fps does not exist: the nearest values are a delay of 3 (33⅓ fps) or 4 (25 fps), and an encoder fed 30 fps alternates between them. Worse, Chrome and Firefox treat a delay of 0 or 1 hundredth (10 ms or less) as 100 ms, so a 60 fps source — whose frames need a delay of 1.67 — yields a file whose 1-hundredth frames are stretched to a tenth of a second. It stutters in a way the original never did; nothing above 50 fps plays correctly in a browser.
The three levers: width, frame rate, duration
Raw GIF data scales with width × height × frames per second × seconds, and the three levers multiply: a 1280-px, 30 fps, 10-second clip cut to 640 px, 12 fps and 5 seconds has a twentieth of the raw data. The table shows the budget the compressor starts from; the file you get is some fraction of it, depending entirely on content.
| Settings | Frames | Pixels per frame | Raw index bytes | Reduction vs first row |
|---|---|---|---|---|
| 1280×720, 30 fps, 10 s | 300 | 921,600 | 276.5 million | 1× |
| 854×480, 15 fps, 10 s | 150 | 409,920 | 61.5 million | 4.5× |
| 640×360, 12 fps, 6 s | 72 | 230,400 | 16.6 million | 16.7× |
| 480×270, 10 fps, 5 s | 50 | 129,600 | 6.5 million | 42.7× |
Width. Chat bubbles and README columns show an image a few hundred CSS pixels wide, so 480–640 px is already full size there and a 1280-px GIF is scaled down on arrival. Frame rate. 10–15 fps reads as smooth for a UI demo or a reaction clip; reserve 20–25 fps for fast motion. Because delays are whole hundredths, 10 fps (delay 10), 12.5 fps (delay 8), 20 fps (delay 5) and 25 fps (delay 4) land exactly; 12 and 15 fps alternate between two delays, which nobody notices. Duration. Six to ten seconds is the ceiling for a chat-sized file. A GIF loops forever, so a clip that ends where it began looks longer than it is — find the loop point rather than adding seconds.
Colours, palettes and dithering
The lazy option is a fixed palette: run ffmpeg -i clip.mp4 out.gif with no filters and the encoder converts to ffmpeg’s bgr8 pixel format — 3 bits of red, 3 of green, 2 of blue, the same 256 colours for every clip — and dithers to hide the gaps. That is the grainy, slightly posterised look people associate with GIFs. The good option is a generated palette: palettegen builds a histogram of the whole clip and keeps the 255 most useful colours (one entry is reserved for transparency by default, reserve_transparent=1), then paletteuse maps every frame onto it. A UI recording usually has fewer than 255 colours in total, so a generated palette reproduces it exactly with no dithering at all.
Dithering is how 256 colours pretend to be more, and the choice shows up in the picture and in the file size. No dither gives banding: visible steps across skies, shadows and gradients. Error diffusion (Floyd–Steinberg and relatives; paletteuse defaults to sierra2_4a) spreads the error into fine noise that looks best in a still but is recomputed for every frame, so static backgrounds shimmer and frame differencing finds nothing to skip. Ordered (Bayer) dithering uses a fixed crosshatch pattern: it stays put on static areas and compresses noticeably better, at the cost of a visible pattern on flat colour at low bayer_scale values. On text, error-diffusion noise crawls along letter edges; use a generated palette with Bayer or no dithering.
What the tools on this site control — and what they do not
Video to GIF
The video to GIF converter takes an MP4, MOV, WebM, MKV or AVI clip and downloads name_output.gif. Two controls in the options dialog matter: Convert to, preset to GIF, and Resolution — Keep original, 360p, 480p, 720p (HD) or 1080p (Full HD). Resolution sets the height and lets the width follow the aspect ratio, rounded to an even number, so 360p on a 16:9 clip is 640×360. The Target file size field other video tools show is hidden for GIF, which has no bitrate to budget. There is no frame-rate control and no palette control: the conversion runs ffmpeg compiled to WebAssembly — GIF is not a format the WebCodecs hardware path can write — with ffmpeg’s built-in GIF encoder at its defaults, so the source frame rate is kept, the fixed bgr8 palette with dithering is used, frame differencing is on, the loop is infinite and audio is discarded. A source taller than 1088 px is capped with the notice High-resolution source — output is capped at 1080p for in-browser conversion on this device. Pick 720p for a smaller, faster file., and a file above 1.5 GB is refused: GIF isn’t available for files this large — it uses the in-browser fallback converter, which can’t load files above ~2 GB. In short: the resolution lever is in this tool, the duration lever is in the trimmer, and the frame-rate lever is not on this site.
Trim
The video trimmer cuts a range out of an MP4, MOV, WebM, MKV or AVI with ffmpeg’s stream copy — -ss START -to END -c copy — so nothing is re-encoded and a long 4K file finishes in seconds. Set Start and End (Use current grabs the playhead; steps are 0.1 s), press Trim & download, and name_trimmed.mp4 arrives in the same container. Stream copy can only begin at a keyframe, so the start may land a fraction of a second early. A range under 0.1 s is rejected with Set an end time later than the start time., and a container that cannot be cut this way reports Could not trim this file. Its format may not allow fast trimming — try converting it first, or a different clip.
Resize
The video resizeroffers the same four heights and keeps the container, so an MP4 stays an MP4. Where the browser can decode the source it re-encodes through the device’s hardware encoder rather than WebAssembly, which makes it the right first step for a 4K or 60 fps phone clip that the GIF converter would otherwise have to decode in software. The resized MP4 is also the file to keep for any destination that accepts video.
Still image to GIF
The image to GIF converter is not an animator. It opens a PNG, JPG, WebP, HEIC, AVIF, SVG or TIFF in the editor (crop, resize, annotate), saves it as a PNG and hands the bytes to ImageMagick, which quantises them to 256 colours and writes a single-frame name_output.gif. Transparency survives only as GIF’s on/off kind — a soft PNG edge is flattened.
The recipe
- Trim first. Cut the clip to six to ten seconds in the trimmer, starting on a frame that works as the poster and ending close to where it began so the loop hides the seam. Trim the video, never the finished GIF.
- Resize if the source is large. A 4K, 60 fps or several-hundred-megabyte clip goes through the resizer at 480p first. Keep that MP4.
- Convert. Drop the trimmed clip on the GIF converter and pick 360p or 480p — never Keep original for a 1080p source.
- Check the size. Under 2–3 MB is comfortable anywhere; up to 8–10 MB is tolerated by chat apps and GitHub. Above that, cut seconds or drop to 360p; if it is still too big, the clip has too much motion for GIF.
With ffmpeg installed you get the frame-rate and palette levers too. This seeks to second 3, keeps six seconds, drops to 12 fps, scales to 480 px wide, generates a palette weighted towards the pixels that change, and applies it with Bayer dithering:
ffmpeg -ss 3 -t 6 -i clip.mp4 \
-vf "fps=12,scale=480:-2:flags=lanczos,split[a][b];[a]palettegen=stats_mode=diff[p];[b][p]paletteuse=dither=bayer:bayer_scale=5" \
-loop 0 out.gif
# flat UI capture with text: exact colours, no dither pattern
# ...paletteuse=dither=none
# gradients or skin tones, accepting a bigger file:
# ...paletteuse=dither=sierra2_4aWhen GIF is the wrong answer
Most places a GIF ends up would rather have a video, and several of them will make one whether you like it or not. Decide by destination:
- A web page you control. Use
<video autoplay muted loop playsinline>with an MP4 (H.264) and optionally a WebM source. It behaves like a GIF, is routinely ten to fifty times smaller, keeps full colour and can carry aposter;mutedandplaysinlineare what let iOS autoplay it. The video compression guide covers getting that MP4 small. - X, WhatsApp, Telegram. All three transcode an uploaded GIF to MP4 before anyone sees it. Upload an MP4 and skip a lossy round-trip through 256 colours.
- A GitHub README. Markdown there renders images, not
<video>, so an animated GIF under the 10 MB image cap is still the way to put motion on the front page. Issues and pull requests accept MP4 and MOV uploads directly. - Slack, Discord, Teams. GIFs play inline — this is where the format earns its keep. Keep them to a few megabytes for mobile clients.
- Email. Animated GIF is the only animation email clients render, but desktop Outlook 2007 through 2016 shows the first frame only, and every recipient downloads the whole file. Stay under a megabyte and make frame one carry the message.
- Full colour or soft transparency. Animated WebP (Safari 14 and iOS 14 onwards; Chrome and Firefox for years) and APNG both do 24-bit colour with an 8-bit alpha channel. The image formats guide covers where each is accepted.
There is an accessibility argument too. WCAG 2.2.2 requires that moving content which starts automatically and lasts more than five seconds can be paused, stopped or hidden; a looping <img> offers none of those and ignores prefers-reduced-motion, whereas a <video> can start paused on its poster for users who ask for reduced motion.
Common mistakes
- Converting at “Keep original”. A 1080p GIF has 2.25× the pixels of 720p and 9× the pixels of 360p, and it will be displayed at roughly the size of the 360p one.
- Leaving a 60 fps screen recording at 60 fps. The converter keeps the source rate, and 60 fps cannot be expressed in hundredths of a second, so the file stutters. Record at 30 fps or lower where the recorder allows it — the screen recording guide covers that — or use the
fps=filter above. - Thirty-second GIFs. Nobody watches past the second loop. Ten seconds is the ceiling; six is better.
- Dithered text. Error-diffusion noise crawls along letter edges and inflates the file. For terminal and UI captures, use a generated palette and Bayer or no dithering.
- Transparent GIFs with a halo. GIF transparency is one bit — a pixel is opaque or absent — so anti-aliased edges keep the colour they were blended with and fringe on any other background. Flatten onto the destination colour, or use animated WebP or APNG.
- Trimming or resizing the GIF instead of the video. Every pass through a GIF encoder re-quantises to 256 colours and re-dithers. Cut and scale the source video, and convert once.
Loops, the first frame, and GIFs as fallbacks
Looping is not part of GIF89a at all; it comes from the NETSCAPE2.0application extension, where a loop count of 0 means forever. ffmpeg’s GIF muxer writes that by default (-loop 0; -loop -1 plays once, and -final_delay holds the last frame before the restart), so a GIF from the converter here loops indefinitely. A GIF has no poster image, so its first frame is what every non-animating context shows — older Outlook, image proxies, a paused preview — which is why the trim step should start on a frame that makes sense alone.
The reverse role also works: a <picture> element can offer an animated WebP with type="image/webp" and fall back to a GIF <img>, and a <video> element can wrap a GIF as fallback content for clients that cannot play video. For platforms that accept nothing but GIF and want a still — a forum avatar, a legacy dashboard — the image to GIF converter produces the single-frame file directly.
Do this
- Trim the source video to six to ten seconds first, starting on a frame that works as the poster.
- Resize a 4K or 60 fps source to 480p before converting, and keep that MP4 for anywhere that accepts video.
- Convert at 360p or 480p — never Keep original — and aim for under 2–3 MB, tolerating up to 8–10 MB for chat and READMEs.
- With ffmpeg, add fps=10–15 and a palettegen/paletteuse pass with Bayer dithering; use no dithering for UI captures with text.
- Ship an MP4 or WebM for web pages and for X, WhatsApp and Telegram, which transcode GIFs anyway; use animated WebP or APNG for full colour or soft alpha.
Frequently asked questions
Why is my GIF bigger than the MP4 it came from?
GIF stores every frame as lossless LZW-compressed indexed pixels with no motion compensation, so a 720p, 30 fps, 10-second clip starts from 276 million bytes of raw pixel indices. The MP4 was small because H.264 describes most frames as changes from the previous one and throws away detail you cannot see; GIF can do neither.
What frame rate should a GIF be?
10 to 15 fps for UI demos and reaction clips, 20 to 25 fps only for fast motion. GIF timing is in hundredths of a second, so 10 fps (delay 10) and 12.5 fps (delay 8) land exactly; 30 fps cannot be represented and becomes 33 or 25 fps.
How do I make a GIF smaller without it looking bad?
Trim it to 6–10 seconds, drop the width to 480–640 px and the frame rate to 10–15 fps — those three multiply, so together they cut the raw data by 20× or more. Then use a generated palette with Bayer dithering rather than a fixed palette, which compresses better and does not shimmer.
Can a GIF have more than 256 colours?
Each frame is limited to one colour table of at most 256 entries, but every frame may carry its own table, so a file as a whole can show more. Encoders rarely do this because the per-frame tables add bytes; if you need true colour and smooth alpha, animated WebP or APNG is the right format.
Does converting a video to GIF keep the audio?
No. The GIF format has no audio track at all, so the converter discards the sound. If the audio matters, share a short MP4 instead, or extract the audio separately.
Tools used in this guide
Every one of these runs in your browser — the files you work on never leave your device.