Skip to content

Instantly share code, notes, and snippets.

@tylerhunt
Last active April 13, 2026 18:46
Show Gist options
  • Select an option

  • Save tylerhunt/d954f8e94113f2f68c61da98cebdeac7 to your computer and use it in GitHub Desktop.

Select an option

Save tylerhunt/d954f8e94113f2f68c61da98cebdeac7 to your computer and use it in GitHub Desktop.
Generate common favicon formats and manifest file.
#!/bin/bash
set -e
# Based on https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs.
#
# Install Dependencies:
#
# brew install imagemagick inkscape
MASK=""
OUTPUT="output"
SOURCE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--mask) MASK=1 ;;
--output) OUTPUT="$2"; shift ;;
-*) echo "Error: unknown flag: $1" >&2; exit 1 ;;
*) SOURCE="$1" ;;
esac
shift
done
if [[ -z "${SOURCE}" ]]; then
echo "Usage: ${0} <icon.svg> [--mask] [--output <dir>]" >&2
exit 1
fi
if [[ "${SOURCE}" != *.svg ]]; then
echo "Error: source must be an SVG file" >&2
exit 1
fi
for cmd in magick inkscape; do
if ! command -v "${cmd}" &> /dev/null; then
echo "Error: '${cmd}' not found." >&2
exit 1
fi
done
# output directory
mkdir -p "${OUTPUT}"
# .svg
cp "${SOURCE}" "${OUTPUT}/icon.svg"
# .ico
ico_inputs=()
for size in 32 16; do
output="${TMPDIR%/}/icon-${size}.png"
inkscape "${SOURCE}" \
--export-width=$size \
--export-filename="${output}"
ico_inputs+=("${output}")
done
magick "${ico_inputs[@]}" "${OUTPUT}/favicon.ico"
# .png
for size in 512 192 ; do
inkscape "${SOURCE}" --export-width=$size --export-filename="${OUTPUT}/icon-$size.png"
done
# apple-touch-icon.png
if [[ -z "${MASK}" ]]; then
inkscape "${SOURCE}" --export-width=180 --export-filename="${OUTPUT}/apple-touch-icon.png"
else
magick "${OUTPUT}/icon-512.png" \
-resize 140x140 \
-background transparent \
-gravity center \
-extent 180x180 \
"${OUTPUT}/apple-touch-icon.png"
fi
# mask .png for Android
if [[ -n "${MASK}" ]]; then
magick "${OUTPUT}/icon-512.png" \
-resize 409x409 \
-background transparent \
-gravity center \
-extent 512x512 \
"${OUTPUT}/icon-mask.png"
fi
# manifest
icons=' { "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },'
if [[ -n "${MASK}" ]]; then
icons+=$'\n { "src": "/icon-512.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable" },'
fi
icons+=$'\n { "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }'
cat << EOF > "${OUTPUT}/manifest.webmanifest"
{
"icons": [
${icons}
]
}
EOF
echo "HTML:"
cat << EOF
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
EOF
echo
echo "For PWA:"
cat << EOF
<link rel="manifest" href="/manifest.webmanifest">
EOF
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment