Last active
April 13, 2026 18:46
-
-
Save tylerhunt/d954f8e94113f2f68c61da98cebdeac7 to your computer and use it in GitHub Desktop.
Generate common favicon formats and manifest file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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