Skip to content

Instantly share code, notes, and snippets.

@clort81
Created July 26, 2026 07:05
Show Gist options
  • Select an option

  • Save clort81/e727365e9f1ba35dcff260dabc054185 to your computer and use it in GitHub Desktop.

Select an option

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
#!/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