Created
July 26, 2026 07:05
-
-
Save clort81/e727365e9f1ba35dcff260dabc054185 to your computer and use it in GitHub Desktop.
ffmpeg to gif animation but better than stackoverflow - better color selection / pallete gen
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Usage: ./mkv2gif.sh input.mkv output.gif [start] [end] [fps] [width] | |
| # Example: ./mkv2gif.sh delme.mkv delout.gif 00:00:22.5 00:01:12 12 640 | |
| # | |
| # | |
| # Option Purpose | |
| # stats_mode=full | |
| # Scans every frame to build one histogram → true global palette. Use diff instead if the background is mostly static (screen recordings) — it only considers pixels that changed, which is usually better for screencasts. | |
| # max_colors=256 | |
| # Fills all 256 slots (default). | |
| # dither=sierra2_4a | |
| # | |
| # Good quality/speed tradeoff. For screen recordings with flat UI colors: | |
| # dither=none or dither=bayer:bayer_scale=5 to avoid grain. | |
| # | |
| # diff_mode=rectangle | |
| # Only re-encodes the changed rectangle per frame → smaller GIFs, ideal for screencasts. | |
| # | |
| # flags=lanczos | |
| # High-quality downscaling. | |
| # | |
| # Recommended variants by content type: | |
| # Screen recording (UI, code, terminal) — usually best quality: | |
| # palettegen=stats_mode=diffpaletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle | |
| # | |
| # photographic | |
| # palettegen=stats_mode=fullpaletteuse=dither=sierra2_4a | |
| # | |
| # Absolutely no dithering (pure color quantization): | |
| # palettegen=stats_mode=fullpaletteuse=dither=none | |
| INPUT="${1:?Usage: $0 input.mkv output.gif [start] [end] [fps] [width]}" | |
| OUTPUT="${2:?Missing output filename}" | |
| START="${3:-0}" | |
| END="${4:-}" | |
| FPS="${5:-12}" | |
| WIDTH="${6:-640}" | |
| # Build time args | |
| TIME_ARGS=(-ss "$START") | |
| [[ -n "$END" ]] && TIME_ARGS+=(-to "$END") | |
| PALETTE="/tmp/palette_$$.png" | |
| trap 'rm -f "$PALETTE"' EXIT | |
| # Common filter chain (keep in sync between passes) | |
| VF="fps=${FPS},scale=${WIDTH}:-1:flags=lanczos" | |
| echo "=== Pass 1: Building global palette from entire clip ===" | |
| ffmpeg -y "${TIME_ARGS[@]}" -i "$INPUT" \ | |
| -vf "${VF},palettegen=max_colors=256:stats_mode=full" \ | |
| "$PALETTE" | |
| echo "=== Pass 2: Encoding GIF with global palette ===" | |
| ffmpeg -y "${TIME_ARGS[@]}" -i "$INPUT" -i "$PALETTE" \ | |
| -lavfi "${VF} [x]; [x][1:v] paletteuse=dither=sierra2_4a:diff_mode=rectangle" \ | |
| -an "$OUTPUT" | |
| echo "Done → $OUTPUT" | |
| ls -lh "$OUTPUT" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment