Skip to content

Instantly share code, notes, and snippets.

@kibotu
Last active March 9, 2026 08:16
Show Gist options
  • Select an option

  • Save kibotu/51f52443be7d6ae6b8f618b04d9599ea to your computer and use it in GitHub Desktop.

Select an option

Save kibotu/51f52443be7d6ae6b8f618b04d9599ea to your computer and use it in GitHub Desktop.
Convert mp4 or mov to webp and gif for optimized for medium articles.
#!/usr/bin/env bash
#
# convert_to_webp.sh — Convert video files to animated GIF and WebP.
#
# DESCRIPTION
# Converts all .mp4 and .mov files in the script's directory to both
# animated GIF (Medium-compatible) and animated WebP (higher quality).
# Output files are placed alongside the source videos.
#
# Aspect ratio is preserved. Videos are scaled to TARGET_WIDTH (default 362px),
# which matches Medium's inline image width.
#
# USAGE
# ./convert_to_webp.sh
#
# OPTIONS
# None. Edit the configuration variables below to adjust output.
#
# CONFIGURATION
# TARGET_WIDTH Output width in pixels (default: 362). Height is auto-calculated.
# FPS Frame rate for the output animations (default: 24).
# GIF_QUALITY Palette size 2–256 (default: 256). Lower = smaller file, worse color.
# GIF_DITHER Dithering algorithm (default: sierra2_4a). Options: none, bayer,
# floyd_steinberg, sierra2, sierra2_4a.
# WEBP_QUALITY Lossy quality 0–100 (default: 82). Higher = better quality, larger file.
# WEBP_METHOD Compression effort 0–6 (default: 4). Higher = slower, better compression.
#
# REQUIREMENTS
# ffmpeg — video decoding, frame extraction, GIF encoding
# ffprobe — reading source video metadata (bundled with ffmpeg)
# img2webp — animated WebP assembly (part of the 'webp' package)
#
# macOS: brew install ffmpeg webp
# Ubuntu: sudo apt install ffmpeg webp
#
# EXAMPLES
# # Place videos in the docs/ folder and run:
# bash docs/convert_to_webp.sh
#
# # Output for input "demo.mp4":
# # docs/demo.gif — upload to Medium
# # docs/demo.webp — use in README / web pages
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TARGET_WIDTH=362
FPS=24
GIF_QUALITY=256
GIF_DITHER=sierra2_4a
WEBP_QUALITY=82
WEBP_METHOD=4
for cmd in ffmpeg ffprobe img2webp; do
if ! command -v "$cmd" &>/dev/null; then
echo "Error: '$cmd' is required but not found in PATH." >&2
echo "Install with: brew install ffmpeg webp (macOS)" >&2
echo " sudo apt install ffmpeg webp (Ubuntu)" >&2
exit 1
fi
done
for src in "$SCRIPT_DIR"/*.mp4 "$SCRIPT_DIR"/*.mov; do
[ -f "$src" ] || continue
filename="$(basename "$src")"
basename="${filename%.*}"
output_gif="$SCRIPT_DIR/${basename}.gif"
output_webp="$SCRIPT_DIR/${basename}.webp"
echo "=== Converting: ${filename} ==="
src_height=$(ffprobe -v quiet -select_streams v:0 \
-show_entries stream=height -of csv=p=0 "$src")
src_width=$(ffprobe -v quiet -select_streams v:0 \
-show_entries stream=width -of csv=p=0 "$src")
echo " Source: ${src_width}x${src_height}"
# --- Animated GIF (Medium-compatible) ---
echo " Creating animated GIF (${FPS} FPS, width ${TARGET_WIDTH})..."
ffmpeg -hide_banner -loglevel warning -y \
-i "$src" \
-vf "fps=${FPS},scale=${TARGET_WIDTH}:-2:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=${GIF_QUALITY}:stats_mode=diff[p];[s1][p]paletteuse=dither=${GIF_DITHER}" \
-loop 0 \
"$output_gif"
gif_size=$(du -h "$output_gif" | cut -f1)
gif_dims=$(ffprobe -v quiet -select_streams v:0 \
-show_entries stream=width,height -of csv=p=0 "$output_gif")
echo " GIF: ${basename}.gif (${gif_size}, ${gif_dims})"
# --- Animated WebP (higher quality fallback) ---
echo " Creating animated WebP..."
FRAME_DIR="$(mktemp -d)"
mkdir -p "$FRAME_DIR/frames"
ffmpeg -hide_banner -loglevel warning -y \
-i "$src" \
-vf "scale=${TARGET_WIDTH}:-2:flags=lanczos" \
-r "$FPS" \
-pix_fmt rgb24 \
"$FRAME_DIR/frames/frame_%06d.png"
frame_count=$(ls "$FRAME_DIR/frames"/frame_*.png 2>/dev/null | wc -l | tr -d ' ')
frame_duration=$((1000 / FPS))
img2webp_args=()
for frame in "$FRAME_DIR/frames"/frame_*.png; do
img2webp_args+=(-d "$frame_duration" -lossy -q "$WEBP_QUALITY" -m "$WEBP_METHOD" "$frame")
done
img2webp -loop 0 "${img2webp_args[@]}" -o "$output_webp"
rm -rf "$FRAME_DIR"
webp_size=$(du -h "$output_webp" | cut -f1)
echo " WebP: ${basename}.webp (${webp_size})"
echo ""
done
echo "Done!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment