compressGIF
compressGIF optimizes GIF files using the gifsicle tool, but only replaces the original when the savings are worthwhile (>10% reduction).
Read this when Use this page when tracing shell automation, compression, upload/download helpers, linting, or preprocessing around compressGIF.
Overview
compressGIF optimizes GIF files using the gifsicle tool, but only replaces the original when the savings are worthwhile (>10% reduction). This threshold prevents unnecessary file churn—gifsicle always modifies file metadata even when no real optimization occurs, which would create spurious git changes.
The script processes files in parallel using GNU parallel for efficient batch optimization.
Usage
# Compress specific files
./compressGIF image1.gif image2.gif
# Compress auto-detected GIFs in current directory (*.gif, non-recursive)
./compressGIF
# Process from a list
find . -name "*.gif" | xargs ./compressGIF
Arguments:
- File paths to compress (optional)
- If no arguments, searches current directory (non-recursive) for
*.gif; if none found, exits silently with status 0 (pipeline-friendly, eg.find … | compressGIF)
How It Works
Optimization Process
Input GIF
↓
Create temp file
↓
gifsicle --colors=256 --optimize=3
↓
Compare sizes
↓
┌─────────────────────────┐
│ Size reduction >= 10%? │
└─────────────────────────┘
↓ Yes ↓ No
Replace original Delete temp
↓ (keep original)
Done
The 10% Threshold
The script calculates:
size_delta = original_size - optimized_size
min_reduction = original_size * 0.1
Only if size_delta >= min_reduction does it replace the original file.
Why this matters:
- gifsicle always changes metadata, even with no real optimization
- Without the threshold, every GIF would show as modified in git
- The 10% threshold ensures only meaningful optimizations are kept
Key Functions
optimize_gif()
Core optimization function applied to each file:
optimize_gif() {
local gif="$1"
# Skip if file doesn't exist
if [ ! -f "$gif" ]; then return; fi
# Skip non-GIF files
if [[ ! "$gif" =~ \.(gif|GIF)$ ]]; then
red "Skipping non-GIF file: $gif"
return
fi
# Create temp file and optimize (delete temp and keep original on failure)
temp_gif="$(mktemp /tmp/XXXXXX.gif)"
if ! gifsicle --colors=256 --optimize=3 "$gif" > "$temp_gif" 2>/dev/null; then
red "gifsicle failed on $gif"
rm -f "$temp_gif"
return
fi
# Compare sizes (empty/corrupted output is also rejected)
original_size="$(stat --printf="%s" "$gif")"
optimized_size="$(stat --printf="%s" "$temp_gif")"
# Replace only if >10% savings
size_delta="$((original_size - optimized_size))"
min_reduction="$(echo "scale=0; $original_size * 0.1 / 1" | bc)"
if [ "$size_delta" -ge "$min_reduction" ]; then
mv "$temp_gif" "$gif"
echo "Optimized $gif"
else
rm "$temp_gif"
fi
}
get_image_files()
Auto-discovers GIF files when no arguments provided (*.gif only, case-insensitive):
find . -maxdepth 1 -type f \
\( -iname "*.gif" \) \
| sort --version-sort
Gifsicle Options
| Option | Purpose |
|---|---|
--colors=256 | Reduce to 256 colors (GIF maximum) |
--optimize=3 | Maximum optimization level |
The -O3 optimization includes:
- Cross-frame optimization
- LZW compression tuning
- Transparency optimization
Dependencies
- gifsicle: GIF manipulation tool
- parallel: GNU parallel for batch processing
- bc: Calculator for percentage math
- stat: File size information
Install on Debian/Ubuntu:
apt-get install gifsicle parallel bc
Helper Functions (Inlined)
The script inlines common helpers for portability:
red() { echo -e "\e[41m$@\e[0m"; } # Red background text
green() { echo -e "\e[32m$@\e[0m"; } # Green text
Error Handling
- Exits immediately on error (
set -e) - Checks for required commands (
gifsicle,parallel) before processing - Gracefully handles non-existent files in
optimize_gif() - Skips non-GIF filenames (extension check) with a warning
- If
gifsiclefails or produces empty output, the temp file is deleted and the original kept - Exits silently with status 0 when no arguments are available (pipeline-friendly)
Known Issue
The script comment references a gifsicle behavior:
NOTE: this also avoids the issue where
gifsiclealways changes the file metadata by clobbering the original, even when no real change was made (which is a WONTFIX by the maintainer: https://github.com/kohler/gifsicle/issues/201)
This is why the threshold-based replacement is essential—without it, every GIF would appear modified in version control.
See Also
- compressPNG - Similar script for PNG compression
- sync.sh - Main build script that may call image optimizers
- Image.hs - Haskell image processing utilities