{"course":{"slug":"motion-and-audio","title":"Motion, Sound and Music","url":"https://addaly.com/learn/motion-and-audio","level":"intermediate","track":"create"},"lesson":{"slug":"gif-is-not-a-video-format","title":"Why a GIF costs twenty times what a video costs","url":"https://addaly.com/learn/motion-and-audio/gif-is-not-a-video-format","position":22,"of":72,"minutes":9,"answer":"GIF stores each frame as a palette of 256 colours with one bit of transparency and no motion compensation between frames.","keyIdea":"GIF stores each frame as a palette of 256 colours with one bit of transparency and no motion compensation between frames, so a clip that a video codec describes as block movement has to be stored almost in full every frame."},"body":{"format":"markdown","text":"## Why the same three seconds is 4MB as a GIF and 180KB as a video\n\nGIF was finalised in 1989. It has three properties that make it unsuitable for the job it is most often used for, and all three follow from that date.\n\n**256 colours, chosen from a palette, per frame.** The format stores an index into a colour table, not a colour. Anything with a gradient, a soft shadow, film grain or skin tone has to be quantised down and then dithered, which is why GIFs of real footage look speckled and banded. The dithering also destroys the compressor's job, because dither noise is by definition unpredictable.\n\n**One bit of transparency.** A pixel is either fully opaque or fully transparent. There is no partial alpha, so an anti-aliased edge over an unknown background gets a hard, jagged fringe in whatever colour the file was matted against. This is the \"why does my logo have a white halo\" problem, and it cannot be fixed in GIF.\n\n**No motion compensation.** Modern video codecs describe a frame as \"the previous frame, with this block moved 14 pixels left\". GIF can only say \"these pixels changed\": a rectangular difference region, compressed with LZW. Pan the camera and every pixel changes, so every frame is stored nearly in full.\n\nPut together: a 640×360 three-second clip that an H.264 encoder stores in about 180KB will commonly be 3 to 6MB as a GIF, and will look worse. That is a factor of twenty to thirty, paid on every page load, by every visitor.\n\n> Figure: The same three seconds, five ways. Bars (KB): GIF 4000; APNG 2400; Animated WebP 900; MP4, H.264 180; Lottie JSON 35. GIF stores 256 palette colours per frame, one bit of transparency, and no motion compensation at all, so a pan means storing nearly every pixel again. The factor of twenty against the video is paid on every page load by every visitor, and the GIF is the one that looks worst. Where the destination demands a GIF anyway, generate the palette from the whole clip rather than one frame.\n\n## Replace it with a video element\n\nThe replacement is a muted, looping, inline video. Autoplay is permitted by every modern browser under the same conditions: the video must be muted and, on iOS, carry `playsinline`.\n\n```html\n<video autoplay loop muted playsinline\n       poster=\"/motion/preview.jpg\"\n       width=\"640\" height=\"360\">\n  <source src=\"/motion/demo.webm\" type=\"video/webm\">\n  <source src=\"/motion/demo.mp4\"  type=\"video/mp4\">\n</video>\n```\n\nTwo sources because VP9 in WebM is usually 20 to 40 per cent smaller than H.264 at the same quality and Safari's support has historically lagged; the MP4 is the guaranteed fallback. Always set `width` and `height`, or the page will shift when the video loads.\n\nThree things this buys you beyond size: the user can pause it, you can respect reduced motion by not autoplaying, and the browser will not decode it while it is off-screen if you add `preload=\"none\"` and start it when it scrolls into view.\n\n## If you are stuck with GIF\n\nSometimes the destination demands it: an email client, a chat platform, a forum that strips video tags. The quality difference between a careless GIF and a careful one is large, and it comes down to the palette.\n\nThe default in most converters is a single palette computed from one frame. The better approach generates a palette from the whole clip, then applies it with dithering:\n\n```bash\nffmpeg -i in.mp4 -vf \"fps=15,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff\" palette.png\nffmpeg -i in.mp4 -i palette.png -lavfi \"fps=15,scale=480:-1:flags=lanczos,paletteuse=dither=bayer:bayer_scale=3\" out.gif\n```\n\n`stats_mode=diff` weights the palette toward the parts of the frame that move, which is where the eye is. Bayer dithering produces a regular pattern that LZW compresses far better than the default error-diffusion dither, usually cutting the file by a third for a small visible cost.\n\nThe other two levers, in order of effect: **drop the frame rate** to 12 to 15, and **drop the resolution**. Both cut size roughly linearly. Trimming a second off the clip is worth more than any encoder setting.\n\n## The middle options\n\nBetween GIF and video there are two raster formats worth knowing.\n\n**Animated WebP**: full 8-bit alpha, far better compression than GIF, supported everywhere that matters now. Typically 30 to 50 per cent of the GIF size at better quality. It is the correct answer when you need an animated *image*: something that works in an `<img>` tag, as a CSS background, or anywhere a video element is not allowed.\n\n**APNG**: full alpha, lossless, and very large. Useful when you need exact pixels and transparency and size is not a concern, which is rare.\n\nBoth are images, so neither can be paused by the user and neither honours reduced motion on its own. That is a real accessibility cost and the reason a video element is preferable where it is allowed.\n\n## The free path\n\n**ffmpeg** does all of it (the conversion, the palette work, the WebP and APNG output, the frame-rate and scale changes) and it is the tool the paid converters are usually wrapping. **Gifski** is an open-source encoder that produces noticeably better GIFs than the default by using a per-frame palette and a smarter quantiser, at the cost of speed. **ImageMagick** handles APNG assembly. None of this needs a licence.\n\n## The limitation\n\nGIF persists for a reason that has nothing to do with quality: it is universally accepted. Email clients, older forums, messaging apps and some content management systems will take a GIF and nothing else, and a technically superior file that the destination rejects is worth nothing.\n\nSo the honest advice is not \"never use GIF\". It is: never use GIF on a web page you control, always use it where the destination demands it, and when you do, spend the two minutes on the palette because the difference is visible and the file is often a third of the size."},"check":{"question":"Converting a screen recording to GIF, a team finds that switching the dither from error-diffusion to Bayer cuts the file by about a third with only a small visible cost. Why does the dithering method change the file size at all?","options":[{"text":"Bayer dithering uses fewer palette entries, and GIF stores one byte per distinct colour used in each frame, so a smaller palette is directly a smaller file.","correct":false,"tempting":"GIF indexes into a palette at a fixed bit depth; the number of entries actually used does not shrink the pixel data the way this implies."},{"text":"Error-diffusion scatters noise unpredictably across the frame, while Bayer produces a regular repeating pattern that the LZW compressor can encode efficiently.","correct":true},{"text":"Bayer dithering is applied once to the whole clip rather than per frame, so the difference regions between frames contain no dither changes and compress to almost nothing.","correct":false,"tempting":"Both dither methods are applied per frame; the difference is the statistical structure of the noise, not how often it is computed."},{"text":"Error-diffusion increases the effective colour depth of the image beyond the 256-colour limit, forcing the encoder to split each frame into several sub-images with their own local colour tables and their own headers.","correct":false,"tempting":"Local colour tables are a real GIF feature, but dithering does not trigger them; it stays inside the existing palette."}]},"prev":{"slug":"svg-animation","title":"Animating a drawing that is also a document","url":"https://addaly.com/learn/motion-and-audio/svg-animation"},"next":{"slug":"alpha-on-the-web","title":"Transparent video, and why it is two files","url":"https://addaly.com/learn/motion-and-audio/alpha-on-the-web"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Why a GIF costs twenty times what a video costs', in Motion, Sound and Music. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/motion-and-audio/gif-is-not-a-video-format (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Why a GIF costs twenty times what a video costs","courseTitle":"Motion, Sound and Music","module":{"index":3,"slug":"shipping-the-motion","title":"Shipping the motion"},"answer":null,"keyIdea":"GIF stores each frame as a palette of 256 colours with one bit of transparency and no motion compensation between frames, so a clip that a video codec describes as block movement has to be stored almost in full every frame.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"bars","unit":"KB","title":"The same three seconds, five ways","series":[{"label":"GIF","value":4000,"highlight":true},{"label":"APNG","value":2400},{"label":"Animated WebP","value":900},{"label":"MP4, H.264","value":180},{"label":"Lottie JSON","value":35}],"caption":"GIF stores 256 palette colours per frame, one bit of transparency, and no motion compensation at all, so a pan means storing nearly every pixel again. The factor of twenty against the video is paid on every page load by every visitor, and the GIF is the one that looks worst. Where the destination demands a GIF anyway, generate the palette from the whole clip rather than one frame."},"alt":"Figure: The same three seconds, five ways. Bars (KB): GIF 4000; APNG 2400; Animated WebP 900; MP4, H.264 180; Lottie JSON 35. GIF stores 256 palette colours per frame, one bit of transparency, and no motion compensation at all, so a pan means storing nearly every pixel again. The factor of twenty against the video is paid on every page load by every visitor, and the GIF is the one that looks worst. Where the destination demands a GIF anyway, generate the palette from the whole clip rather than one frame."}},"blocks":[{"t":"h2","id":"why-the-same-three-seconds-is-4mb-as-a-gif-and-180kb-as-a-vi","runs":[{"text":"Why the same three seconds is 4MB as a GIF and 180KB as a video"}]},{"t":"p","runs":[{"text":"GIF was finalised in 1989. It has three properties that make it unsuitable for the job it is most often used for, and all three follow from that date."}]},{"t":"p","runs":[{"text":"256 colours, chosen from a palette, per frame.","b":true},{"text":" The format stores an index into a colour table, not a colour. Anything with a gradient, a soft shadow, film grain or skin tone has to be quantised down and then dithered, which is why GIFs of real footage look speckled and banded. The dithering also destroys the compressor’s job, because dither noise is by definition unpredictable."}]},{"t":"p","runs":[{"text":"One bit of transparency.","b":true},{"text":" A pixel is either fully opaque or fully transparent. There is no partial alpha, so an anti-aliased edge over an unknown background gets a hard, jagged fringe in whatever colour the file was matted against. This is the “why does my logo have a white halo” problem, and it cannot be fixed in GIF."}]},{"t":"p","runs":[{"text":"No motion compensation.","b":true},{"text":" Modern video codecs describe a frame as “the previous frame, with this block moved 14 pixels left”. GIF can only say “these pixels changed”: a rectangular difference region, compressed with LZW. Pan the camera and every pixel changes, so every frame is stored nearly in full."}]},{"t":"p","runs":[{"text":"Put together: a 640×360 three-second clip that an H.264 encoder stores in about 180KB will commonly be 3 to 6MB as a GIF, and will look worse. That is a factor of twenty to thirty, paid on every page load, by every visitor."}]},{"t":"h2","id":"replace-it-with-a-video-element","runs":[{"text":"Replace it with a video element"}]},{"t":"p","runs":[{"text":"The replacement is a muted, looping, inline video. Autoplay is permitted by every modern browser under the same conditions: the video must be muted and, on iOS, carry "},{"text":"playsinline","code":true},{"text":"."}]},{"t":"code","lang":"html","label":"HTML","text":"<video autoplay loop muted playsinline\n       poster=\"/motion/preview.jpg\"\n       width=\"640\" height=\"360\">\n  <source src=\"/motion/demo.webm\" type=\"video/webm\">\n  <source src=\"/motion/demo.mp4\"  type=\"video/mp4\">\n</video>"},{"t":"p","runs":[{"text":"Two sources because VP9 in WebM is usually 20 to 40 per cent smaller than H.264 at the same quality and Safari’s support has historically lagged; the MP4 is the guaranteed fallback. Always set "},{"text":"width","code":true},{"text":" and "},{"text":"height","code":true},{"text":", or the page will shift when the video loads."}]},{"t":"p","runs":[{"text":"Three things this buys you beyond size: the user can pause it, you can respect reduced motion by not autoplaying, and the browser will not decode it while it is off-screen if you add "},{"text":"preload=\"none\"","code":true},{"text":" and start it when it scrolls into view."}]},{"t":"h2","id":"if-you-are-stuck-with-gif","runs":[{"text":"If you are stuck with GIF"}]},{"t":"p","runs":[{"text":"Sometimes the destination demands it: an email client, a chat platform, a forum that strips video tags. The quality difference between a careless GIF and a careful one is large, and it comes down to the palette."}]},{"t":"p","runs":[{"text":"The default in most converters is a single palette computed from one frame. The better approach generates a palette from the whole clip, then applies it with dithering:"}]},{"t":"code","lang":"bash","label":"Bash","text":"ffmpeg -i in.mp4 -vf \"fps=15,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff\" palette.png\nffmpeg -i in.mp4 -i palette.png -lavfi \"fps=15,scale=480:-1:flags=lanczos,paletteuse=dither=bayer:bayer_scale=3\" out.gif"},{"t":"p","runs":[{"text":"stats_mode=diff","code":true},{"text":" weights the palette toward the parts of the frame that move, which is where the eye is. Bayer dithering produces a regular pattern that LZW compresses far better than the default error-diffusion dither, usually cutting the file by a third for a small visible cost."}]},{"t":"p","runs":[{"text":"The other two levers, in order of effect: "},{"text":"drop the frame rate","b":true},{"text":" to 12 to 15, and "},{"text":"drop the resolution","b":true},{"text":". Both cut size roughly linearly. Trimming a second off the clip is worth more than any encoder setting."}]},{"t":"h2","id":"the-middle-options","runs":[{"text":"The middle options"}]},{"t":"p","runs":[{"text":"Between GIF and video there are two raster formats worth knowing."}]},{"t":"p","runs":[{"text":"Animated WebP","b":true},{"text":": full 8-bit alpha, far better compression than GIF, supported everywhere that matters now. Typically 30 to 50 per cent of the GIF size at better quality. It is the correct answer when you need an animated "},{"text":"image","i":true},{"text":": something that works in an "},{"text":"<img>","code":true},{"text":" tag, as a CSS background, or anywhere a video element is not allowed."}]},{"t":"p","runs":[{"text":"APNG","b":true},{"text":": full alpha, lossless, and very large. Useful when you need exact pixels and transparency and size is not a concern, which is rare."}]},{"t":"p","runs":[{"text":"Both are images, so neither can be paused by the user and neither honours reduced motion on its own. That is a real accessibility cost and the reason a video element is preferable where it is allowed."}]},{"t":"h2","id":"the-free-path","runs":[{"text":"The free path"}],"role":"free"},{"t":"p","runs":[{"text":"ffmpeg","b":true},{"text":" does all of it (the conversion, the palette work, the WebP and APNG output, the frame-rate and scale changes) and it is the tool the paid converters are usually wrapping. "},{"text":"Gifski","b":true},{"text":" is an open-source encoder that produces noticeably better GIFs than the default by using a per-frame palette and a smarter quantiser, at the cost of speed. "},{"text":"ImageMagick","b":true},{"text":" handles APNG assembly. None of this needs a licence."}]},{"t":"h2","id":"the-limitation","runs":[{"text":"The limitation"}],"role":"limit"},{"t":"p","runs":[{"text":"GIF persists for a reason that has nothing to do with quality: it is universally accepted. Email clients, older forums, messaging apps and some content management systems will take a GIF and nothing else, and a technically superior file that the destination rejects is worth nothing."}]},{"t":"p","runs":[{"text":"So the honest advice is not “never use GIF”. It is: never use GIF on a web page you control, always use it where the destination demands it, and when you do, spend the two minutes on the palette because the difference is visible and the file is often a third of the size."}]}],"check":{"question":[{"text":"Converting a screen recording to GIF, a team finds that switching the dither from error-diffusion to Bayer cuts the file by about a third with only a small visible cost. Why does the dithering method change the file size at all?"}],"options":[{"text":[{"text":"Bayer dithering uses fewer palette entries, and GIF stores one byte per distinct colour used in each frame, so a smaller palette is directly a smaller file."}],"correct":false,"tempting":[{"text":"GIF indexes into a palette at a fixed bit depth; the number of entries actually used does not shrink the pixel data the way this implies."}]},{"text":[{"text":"Error-diffusion scatters noise unpredictably across the frame, while Bayer produces a regular repeating pattern that the LZW compressor can encode efficiently."}],"correct":true,"tempting":null},{"text":[{"text":"Bayer dithering is applied once to the whole clip rather than per frame, so the difference regions between frames contain no dither changes and compress to almost nothing."}],"correct":false,"tempting":[{"text":"Both dither methods are applied per frame; the difference is the statistical structure of the noise, not how often it is computed."}]},{"text":[{"text":"Error-diffusion increases the effective colour depth of the image beyond the 256-colour limit, forcing the encoder to split each frame into several sub-images with their own local colour tables and their own headers."}],"correct":false,"tempting":[{"text":"Local colour tables are a real GIF feature, but dithering does not trigger them; it stays inside the existing palette."}]}],"order":[0,3,2,1],"why":null},"prev":{"slug":"svg-animation","title":"Animating a drawing that is also a document","minutes":10},"next":{"slug":"alpha-on-the-web","title":"Transparent video, and why it is two files","minutes":10}}}