GIF
37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0GIF (CompuServe, 1987) is an indexed-colour format with LZW compression: at most 256 colours per frame, one fully transparent colour at most, no partial alpha. It survives because every chat app, forum and browser plays it without asking. Compression is poor by modern standards, so GIFs of video-like content are many times larger than the same clip as video, animated WebP, animated AVIF or APNG.
| Property | Value |
|---|---|
| Compression | Lossless LZW of palette indices (making the palette is the lossy step) |
| Colours | 256 per frame (a global table plus optional per-frame tables) |
| Transparency | One fully transparent palette index, no partial alpha |
| Animation | Yes, frame delays in hundredths of a second, loop count |
| Royalty-free | Yes |
When to use it
Section titled “When to use it”- Short loops that must work in apps that accept nothing else: chat, forums, issue trackers, email.
- Small flat-colour animations, pixel art and UI demos with few colours and little motion.
- Don’t use it for screen recordings or clips of film or games if you control the page. A muted, looping
<video>(MP4 or WebM) is far smaller and keeps full colour. Animated WebP or AVIF is the middle path when you need an<img>.
Make a GIF with FFmpeg
Section titled “Make a GIF with FFmpeg”Converting straight to GIF makes FFmpeg use a generic palette, which looks dithered and muddy. Generate a palette from the clip and apply it. In one command:
ffmpeg -i input.mp4 -vf "fps=15,scale=480:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" output.gifor in two steps, which lets you reuse or inspect the palette:
ffmpeg -i input.mp4 -vf "fps=15,scale=480:-1:flags=lanczos,palettegen" palette.pngffmpeg -i input.mp4 -i palette.png -lavfi "fps=15,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse" output.gifFrame rate and width have the biggest effect on size. Use a trim (-ss and -t) to keep clips short.
| Option | Use |
|---|---|
palettegen=stats_mode=diff | Weights colours towards the parts of the image that change. Better when the background is static (screen recordings). |
palettegen=stats_mode=single with paletteuse=new=1 | One palette per frame. Helps when colours change a lot between scenes, but frames carry their own palettes and the file usually grows. |
paletteuse=dither=bayer:bayer_scale=5 | Ordered dithering gives much smaller files and no shimmering noise, at the cost of a visible crosshatch pattern. Default is sierra2_4a, which looks smoother but compresses worse. |
paletteuse=diff_mode=rectangle | Only re-encodes the changed rectangle of each frame. Helps whenever part of the picture is static. |
palettegen=max_colors=128 | Fewer colours, smaller file. |
-loop | 0 loops forever (the default), -1 plays once. |
Frame delays are stored in centiseconds, so 15 fps (6.67 cs) is approximated, and browsers play delays of 1 cs or less as 10 cs, so 50 fps is the ceiling. 25 fps (4 cs) and 20 fps (5 cs) are exact.
gifski
Section titled “gifski”gifski (AGPL-3.0) produces much better-looking GIFs than FFmpeg’s filters by using per-frame palettes with temporal dithering, so frames can use thousands of colours across a clip.
ffmpeg -i input.mp4 -f yuv4mpegpipe - | gifski --fps 15 --width 640 -o output.gif -gifski --quality 80 --width 640 --fps 15 -o output.gif frame*.pnggifski limits animations to about 800×600 unless you pass --width. --fps defaults to 20: video input (Y4M) is resampled to that rate by dropping or duplicating frames, so set it in gifski rather than with an FFmpeg fps filter; for PNG frames it sets the playback speed. To shrink the output, reduce --width first (the biggest effect), then --quality (default 90), --lossy-quality and --motion-quality. If the source was lossy video, downscale it too: compression noise inflates the file.
Optimise an existing GIF with Gifsicle
Section titled “Optimise an existing GIF with Gifsicle”gifsicle -O3 input.gif -o output.gifgifsicle -O3 --lossy=80 --colors 128 input.gif -o output.gif-O3 is lossless frame-rectangle optimisation. --lossy (bare = 20) trades noise for size, and --colors re-quantises the palette. Use gifsicle --info to inspect frames, delays and disposal. Quote frame selectors such as '#0' so the shell doesn’t read them as comments.
Pitfalls
Section titled “Pitfalls”- Palette per scene: a single global palette over a clip with different scenes looks bad; use
stats_mode=singleor split the clip. - Transparency has no soft edges: antialiased cut-outs get a halo against backgrounds other than the one they were made on. Use
paletteuse=alpha_threshold=...and test on light and dark backgrounds, or use APNG/WebP. - Resizing after palette creation adds colours that don’t exist in the palette. Scale before
palettegen, as in the commands above. - Services that convert GIFs to video anyway: if the service accepts a video, upload that instead.
Full option reference
Section titled “Full option reference”Every option and default for Gifsicle 1.96 and the FFmpeg 9.0.2 GIF encoder, muxer and palette filters. Reading option tables explains the Default column.
Complete Gifsicle parameter reference
Section titled “Complete Gifsicle parameter reference”Most frame options apply to the inputs that follow them, so order matters. A default is the state when the option is omitted; --same-... resets an earlier override. Bare --lossy means 20, bare --optimize means level 1 and bare --threads means 8.
All Gifsicle options, aliases and omission defaults
| Parameter / aliases | Default | Values / effect |
|---|---|---|
--append | not invoked | Appends frames from the following GIFs to the end of the input. |
--app-extension, -x | not invoked | Adds an application extension (name, data) to the next frames. |
--no-app-extensions | off | Removes application extensions from the input. |
--background, -B, +B, --no-background, --bg, --no-bg | input-dependent | Sets the background colour, as a palette index or colour; the negated form picks index 0. |
--batch, -b | off | Modifies the named input files in place; overwrites every input. |
--careful, --no-careful | off | Writes larger GIFs that avoid bugs in some other programs. |
--change-color, --no-change-color | none | Replaces one colour with another throughout: —change-color COL1 COL2. |
--color-info, --cinfo, --no-cinfo, --no-color-info | off | Adds palette details to —info output. |
--crop, --clip, --no-clip, --no-crop | none | Crops frames to X,Y+WxH or X,Y-X2,Y2. |
--colors, -k, +k, --no-colors | 0 (no reduction) | Reduces the palette to N colours (2–256); changes pixels. |
--color-method, --method | diversity | Palette selection used by —colors: diversity, blend-diversity or median-cut. |
--comment, -c | none added | Adds a comment before the next frame. |
--no-comments, +c | off | Removes comments from the input; +c is the same. |
--conserve-memory, --no-conserve-memory | 0 (not requested) | Uses less memory at the cost of speed. |
--crop-transparency, --no-crop-transparency | off | Crops transparent borders off the image. |
--delay, -d, +d, --no-delay | input-dependent (-1) | Frame delay in hundredths of a second (16-bit). |
--delete | not invoked | Deletes the selected frames until —done. |
--disposal, -D, +D, --no-disposal | input-dependent (-1) | Frame disposal: none/0, asis/1, background/2, previous/3; 4–7 are reserved. |
--dither, -f, +f, --no-dither | none | Dithers after a palette change; optional method name (see below), bare means default. |
--done | not invoked | Ends a —delete, —insert-before, —append or —replace operation. |
--explode, -e | off | Writes each frame to its own file, named input.frame-number. |
--explode-by-name, -E | off | Like —explode, but names files after the stored frame names. |
--extension | not invoked | Adds a raw extension: —extension NUMBER DATA. |
--no-extension, --no-extensions, +x | off | Removes non-application extensions from the input; +x is the same. |
--extension-info, --no-extension-info, --xinfo, --no-xinfo | off | Adds extension details to —info output. |
--flip-horizontal, --no-flip-horizontal | off | Mirrors the following frames left to right. |
--flip-vertical, --no-flip-vertical | off | Mirrors the following frames top to bottom. |
--no-flip | not invoked | Clears pending flips. |
--gamma, --no-gamma | sRGB | Gamma for colour reduction: sRGB, a number, or oklab on builds that support them. |
--gray | off | Reduces to a grayscale palette; same as —use-colormap gray. |
--help, -h | not invoked | Prints help and exits. |
--ignore-errors, --no-ignore-errors | on | Keeps going after recoverable input errors; —no-ignore-errors quits on badly damaged GIFs. |
--info, -I, +I, --no-info | off | Prints input information and suppresses GIF output; -II keeps the output. |
--insert-before | not invoked | Inserts the following GIFs before the selected frame. |
--interlace, -i, +i, --no-interlace | input-dependent (-1) | Writes interlaced frames; +i turns it off. |
--logical-screen, -S, +S, --no-logical-screen, --screen, --no-screen | input-dependent | Canvas size WxH; the negated form recomputes it from the frames. |
--loopcount, -l, +l, --no-loopcount | input-dependent (-2) | Repeats after the first play: =1 shows the animation twice; bare or forever loops endlessly. |
--lossy | 0 | Trades colour accuracy for smaller files, adding noise; bare means 20. |
--merge, -m | on (normal mode) | Combines all inputs into one GIF; this is the default mode. |
--multifile, --no-multifile | off | Reads concatenated GIF streams from one file. |
--name, -n | none added | Sets the next frame’s name. |
--nextfile, --no-nextfile | off | Reads concatenated GIF streams, one stream per input. |
--no-names, +n | off | Removes frame names from the input; +n is the same. |
--optimize, -O, +O, --no-optimize | 0 | Shrinks animations: 1 stores changed areas, 2 adds transparency, 3 tries more methods; bare means 1. |
--output, -o | mode-dependent | Output file; - means stdout. |
--position, -p, +p, --no-position | input-dependent (-1,-1) | Frame offset as X,Y; the negated form sets 0,0. |
--replace | not invoked | Replaces the selected frames with the following GIFs. |
--resize, --no-resize, --resiz, --no-resiz, --resi, --no-resi, --res, --no-res | none | Resizes to WxH; 0 for one side keeps the aspect ratio, both sides can change it. |
--resize-width, --no-resize-width | none | Resizes to this width, keeping the aspect ratio. |
--resize-height, --no-resize-height | none | Resizes to this height, keeping the aspect ratio. |
--resize-fit, --no-resize-fit, --resize-fi, --no-resize-fi, --resize-f, --no-resize-f | none | Shrinks to fit inside WxH, keeping the aspect ratio; never enlarges. |
--resize-fit-width, --no-resize-fit-width | none | Shrinks to this width if wider, keeping the aspect ratio. |
--resize-fit-height, --no-resize-fit-height | none | Shrinks to this height if taller, keeping the aspect ratio. |
--resize-touch, --no-resize-touch, --resize-touc, --no-resize-touc, --resize-tou, --no-resize-tou, --resize-to, --no-resize-to, --resize-t, --no-resize-t | none | Resizes up or down until it touches WxH, keeping the aspect ratio. |
--resize-touch-width, --no-resize-touch-width | none | Resizes to this width, enlarging if needed, keeping the aspect ratio. |
--resize-touch-height, --no-resize-touch-height | none | Registered, but 1.96 treats the value as a width; use —resize-touch WxH. |
--resize-geometry, --no-resize-geometry | none | Resizes using a geometry string with optional fit, fill and percentage modifiers. |
--resize-method, --scale-method | mix | Resampling filter: point, mix, box, catrom, lanczos2, lanczos3 or mitchell. |
--resize-colors, --no-resize-colors, --scale-colors, --no-scale-colors | 0 | Lets resizing add colours up to N (at most 256). |
--rotate-90 | off | Rotates the following frames by 90 degrees. |
--rotate-180 | off | Rotates the following frames 180 degrees. |
--rotate-270 | off | Rotates the following frames by 270 degrees. |
--no-rotate | not invoked | Clears the pending rotation. |
--same-app-extensions | not invoked | Goes back to the source’s application extensions. |
--same-background, --same-bg | not invoked | Goes back to the source’s background colour. |
--same-clip, --same-crop | not invoked | Goes back to the source’s frame size, with no crop. |
--same-comments | not invoked | Goes back to the source’s comments. |
--same-extensions | not invoked | Goes back to the source’s extensions. |
--same-interlace | not invoked | Goes back to the source’s interlacing. |
--same-logical-screen, --same-screen | not invoked | Goes back to the source’s canvas size. |
--same-loopcount | not invoked | Goes back to the source’s loop count. |
--same-disposal | not invoked | Goes back to the source’s frame disposal. |
--same-delay | not invoked | Goes back to the source’s frame delays. |
--same-names | not invoked | Goes back to the source’s frame names. |
--same-position | not invoked | Goes back to the source’s frame positions. |
--same-transparent | not invoked | Goes back to the source’s transparent colour. |
--scale, --no-scale | none | Scales by X or XxY factors. |
--size-info, --sinfo, --no-sinfo, --no-size-info | off | Adds compression details to —info output. |
--transform-colormap, --no-transform-colormap | none | Pipes each output palette through a shell command; never insert untrusted text. |
--transparent, -t, +t, --no-transparent | input-dependent | Makes a colour transparent, by palette index or colour; the negated form removes transparency. |
--unoptimize, -U, +U, --no-unoptimize | off | Expands optimized frames into full images before editing. |
--use-colormap, --no-use-colormap | none | Uses web, gray, bw or a GIF file’s palette; output may use only part of it. |
--use-exact-colormap, --no-use-exact-colormap | none | Same palette sources, but keeps the whole palette. |
--verbose, -V, +V, --no-verbose, -v, +v | off | Prints progress, listing files read and written. |
--version | not invoked | Prints the version and exits. |
--warnings, -w, +w, --no-warnings | on | -w hides warnings, +w shows them. |
--threads, -j, +j, --no-threads | 0 | Uses N threads (bare means 8); needs a build with thread support. |
FILE | stdin when no files are named | Input GIF; reads stdin when none is named. |
FRAME | all input frames | Frame selector: #num, #first-last, #first- or #name; quote it so the shell keeps the #. |
-- | not invoked | Ends options so later arguments are filenames. |
Dither names: none/posterize, default, floyd-steinberg/fs, atkinson/at, o3, o4, o8, ro64/ordered, diag45, halftone, sqhalftone. Comma-separated numbers select matrix size or level count (for example o,3).
--resize-touch-height is registered but dispatches its value as a width in 1.96, so use --resize-touch WxH. The initial gamma mode is sRGB although the help prints [2.2].
Complete FFmpeg GIF encoder options
Section titled “Complete FFmpeg GIF encoder options”| Parameter / aliases | Default | Values / effect |
|---|---|---|
-gifflags | offsetting+transdiff | offsetting (1) crops each frame to the changed area; transdiff (2) makes unchanged pixels transparent. |
-gifimage | false | Writes each frame as its own complete single-image GIF. |
-global_palette | true | Writes a global palette in the header when the frames allow it. |
Complete FFmpeg GIF muxer options
Section titled “Complete FFmpeg GIF muxer options”Loop counts and the last frame’s delay change presentation, not decoded colours.
| Parameter / aliases | Default | Values / effect |
|---|---|---|
-loop | 0 | Loop count, -1 to 65535: 0 loops forever, -1 omits the loop extension. |
-final_delay | -1 | Hold time after the last frame in hundredths of a second; -1 uses that frame’s own duration. |
Complete palettegen filter options
Section titled “Complete palettegen filter options”Filter syntax is palettegen=key=value:key=value.
| Parameter / aliases | Default | Values / effect |
|---|---|---|
max_colors | 256 | Palette size limit, 2–256; reserve_transparent uses one of the slots. |
reserve_transparent | true | Reserves a palette entry for transparency; set 0 for opaque sources to gain a colour. |
transparency_color | lime | Colour stored in the reserved transparent entry. |
stats_mode | full | full (0) analyzes every frame, diff (1) only changed regions, single (2) builds one palette per frame. |
Complete paletteuse filter options
Section titled “Complete paletteuse filter options”debug_kdtree is a debugging option outside the stable filter API.
| Parameter / aliases | Default | Values / effect |
|---|---|---|
dither | sierra2_4a | Dither algorithm by name or number: 0 off, 1 bayer, 2 heckbert, 3 floyd_steinberg, 4 sierra2, 5 sierra2_4a, 6 sierra3, 7 burkes, 8 atkinson. |
bayer_scale | 2 | 0–5 for dither=bayer: lower shows more crosshatch, higher shows more banding. |
diff_mode | 0 | rectangle (1) re-maps only the area that changed since the last frame; 0 maps whole frames. |
new | false | Takes a new palette for every frame; pair with palettegen stats_mode=single. |
alpha_threshold | 128 | Pixels with alpha below this value, 0–255, become transparent. |
debug_kdtree | unset | Writes a Graphviz kd-tree dump to this path; for debugging only. |