Library
Optimize Images
Turn a folder of photos into WebP width variants with ImageMagick. Smaller sources are not upscaled, and existing files are skipped.
#!/usr/bin/env bash
set -euo pipefail
find_magick() {
if command -v magick >/dev/null 2>&1; then
echo magick
return
fi
if command -v convert >/dev/null 2>&1 \
&& convert -version 2>/dev/null | grep -qi imagemagick; then
echo convert
return
fi
echo "ImageMagick not found. Install magick and add it to PATH." >&2
exit 1
}
optimize_one() {
local src="$1"
local dest_stem="$2"
local magick="$3"
local index width quality dest
mkdir -p "$(dirname "$dest_stem")"
for index in "${!WIDTHS[@]}"; do
width="${WIDTHS[$index]}"
quality="${QUALITIES[$index]:-${QUALITIES[-1]}}"
dest="${dest_stem}-${width}.${FORMAT}"
if [[ -f "$dest" && "$FORCE" -eq 0 ]]; then
echo "EXISTS $dest"
continue
fi
echo "CREATE $dest"
"$magick" "$src" \
-auto-orient \
-strip \
-resize "${width}x>" \
-quality "$quality" \
"$dest"
done
}
INPUT="${1:?Usage: optimize-images <input> <output>}"
OUTPUT="${2:?Usage: optimize-images <input> <output>}"
INPUT="${INPUT%/}"
OUTPUT="${OUTPUT%/}"
FORMAT="${FORMAT:-webp}"
FORCE="${FORCE:-0}"
WIDTHS=(${WIDTHS:-480 960 1600})
QUALITIES=(${QUALITIES:-55 72 80})
magick="$(find_magick)"
mkdir -p "$OUTPUT"
if [[ -f "$INPUT" ]]; then
optimize_one "$INPUT" "$OUTPUT/$(basename "${INPUT%.*}")" "$magick"
exit 0
fi
if [[ ! -d "$INPUT" ]]; then
echo "input not found: $INPUT" >&2
exit 1
fi
while IFS= read -r -d '' src; do
rel="${src#"$INPUT"/}"
optimize_one "$src" "$OUTPUT/${rel%.*}" "$magick"
done < <(
find "$INPUT" \
\( -name .git -o -name node_modules -o -name optimized \) -prune -o \
-type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \) \
-print0
)
chmod +x optimize-images
./optimize-images ./assets ./assets/optimized
WIDTHS="320 640 1280" QUALITIES="50 65 78" FORMAT=webp \
./optimize-images ./photos ./out
# assets/hero/station.jpg →
# assets/optimized/hero/station-480.webp
# assets/optimized/hero/station-960.webp
# assets/optimized/hero/station-1600.webp
What it does
- Needs ImageMagick 7+ as magick on PATH. Windows convert.exe is ignored on purpose.
- ${width}x> resizes down only. A 700px photo will not be stretched to 1600px.
- Existing outputs are skipped unless FORCE=1. This is a local batch tool, not an upload compressor.
Where it fits
A practical place for this snippet in a real project.
Useful before publishing a static site, so the HTML srcset can point at 480, 960, and 1600 WebP files. It is not a CDN image service.