Published on, Time to read
🕒 6 min read

Generate PNG icons assets for your website (Favicon, Apple Touch Icon, etc.) from SVG file

Generate PNG icons assets for your website (Favicon, Apple Touch Icon, etc.) from SVG file

Introduction

Working on a website, I usually face the need to generate a set of icons for different devices and platforms. during my developer journey, I used third-party tools, like Photoshop, Figma, or online services to generate these icons.

But I love simple and automated solutiones (because I'm a developer m'kay? 😄). So I decided to create a simple script that will generate all the necessary icons from a single SVG file.

I won't lie, I was inspired by some existing solutions I found on the internet a time ago. It would be a good manner to give credits to its authors, however I lost the links to all of them, because I've been using my script for a long time now.

TL;DR

If you are in a hurry, and just want to get the script -> copy it from the gist https://gist.github.com/mausic/svg-to-png-icons

For detailed explanation, keep reading.

Preparation

Note

This guide is for MacOS users, but you can easily adapt it to other operating systems, simply by installing the required tools from official websites or package managers of choise

We will use the following tools:

  • librsvg (to convert SVG to PNG)
  • ImageMagick (to manipulate PNG images, and generate favicon.ico)
  • pngquant (to optimize PNG images file size)

I use homebrew to manage my packages, so I install all the required tools using it:

brew install librsvg imagemagick pngquant

Drink some coffe or tea while the installation is in progress ☕️ 🍵

Script

I created a simple bash script, that I will explain step by step:

Define variables

We will call our script with two arguments:

  1. The path to the SVG icon file
  2. The path to the directory where we want to save the generated icons

So we will use those variable to identify the constants in the script:

# get the source SVG icon path variable ICON_PATH from the first argument
ICON_PATH="$PWD/$1"
# get the destination path for the icons FAICON_PATH from the second argument
FAVICON_PATH="$PWD/$2"
# show us the paths (to check if everything is correct)
printf "Icon path: $ICON_PATH\n"
printf "Favicon path: $FAVICON_PATH\n"
# get the SVG icon file name (with extension)
ICON_BASE=$(basename "$ICON_PATH")
printf "Icon base: $ICON_BASE\n"
# get the SVG icon file name (without extension)
ICON_FILE="${ICON_BASE%.*}"
printf "Icon file: $ICON_FILE\n"
# specify the favicon file name (we are talking about favicon.ico)
FAVICON_FILE="favicon"

Crete PNG icons from SVG

# For every dimension in this list, create a square PNG icon
for size in 16 32 48 150 180 192 512; do
  # specify the output file name
  ICON_OUT=$ICON_FILE-${size}.png
  # specify the dimensions of the icon
  DIMENSIONS=${size}x${size}
  # use rsvg-convert to create the PNG icon
  rsvg-convert -w "$size" -p 300 -d 300 "$ICON_PATH" > "$FAVICON_PATH/$ICON_OUT"
  printf "Created $FAVICON_PATH/$ICON_OUT\n"
  # Use ImageMagick to center the image and make it square
  magick "$FAVICON_PATH/$ICON_OUT" -gravity center -background transparent -resize "$DIMENSIONS" -extent "$DIMENSIONS" "$FAVICON_PATH/temp-$ICON_OUT"
  printf "Resized $FAVICON_PATH/temp-$ICON_OUT\n"
  # Use pngquant to reduce the size of the PNG
  pngquant 256 < "$FAVICON_PATH/temp-$ICON_OUT" > "$FAVICON_PATH/$FAVICON_FILE-$DIMENSIONS.png"
  printf "Quantized $FAVICON_PATH/$FAVICON_FILE-$DIMENSIONS.png\n"
done

Create favicon.ico

# Merge the 16, 32 and 48 pixel versions into a multi-sized ICO file
magick \
  $FAVICON_PATH/$FAVICON_FILE-16x16.png \
  $FAVICON_PATH/$FAVICON_FILE-32x32.png \
  $FAVICON_PATH/$FAVICON_FILE-48x48.png \
  -background transparent \
  $FAVICON_PATH/$FAVICON_FILE.ico
printf "Created $FAVICON_PATH/$FAVICON_FILE.ico\n"

Create Apple Touch Icon, Android Chrome Icons, and MS Tile Icons

# Create Apple touch icons
mv $FAVICON_PATH/$FAVICON_FILE-180x180.png $FAVICON_PATH/apple-touch-icon.png
printf "Created $FAVICON_PATH/apple-touch-icon.png\n"

# Create Android Chrome icons
mv $FAVICON_PATH/$FAVICON_FILE-192x192.png $FAVICON_PATH/android-chrome-192x192.png
printf "Created $FAVICON_PATH/android-chrome-192x192.png\n"
mv $FAVICON_PATH/$FAVICON_FILE-512x512.png $FAVICON_PATH/android-chrome-512x512.png
printf "Created $FAVICON_PATH/android-chrome-512x512.png\n"

# Create MS tile icon
mv $FAVICON_PATH/$FAVICON_FILE-150x150.png $FAVICON_PATH/mstile-150x150.png
printf "Created $FAVICON_PATH/mstile-150x150.png\n"

Result

After everything is explained, you can create a shell script file, like generate-icons.sh, and paste the folloing code:

#!/usr/bin/env bash
# Version: 1.0.0
# Author: https://github.com/mausic
# Source: https://gist.github.com/mausic/1dba573afaf06d4bec51f2a79d3624a3
# Description: Generate favicons for website from SVG logo.
# Also creates browserconfig.xml and site.webmanifest files.
# Dependencies: ImageMagick, pngquant, librsvg
# Usage: ./generate-icons.sh <source> <destination>
# Example: ./generate-icons.sh public/logo.svg public

ICON_PATH="$PWD/$1"
FAVICON_PATH="$PWD/$2"

printf "Icon path: $ICON_PATH\n"
printf "Favicon path: $FAVICON_PATH\n"

ICON_BASE=$(basename "$ICON_PATH")
printf "Icon base: $ICON_BASE\n"
ICON_FILE="${ICON_BASE%.*}"
printf "Icon file: $ICON_FILE\n"

FAVICON_FILE="favicon"

# Use rsvg-convert to create crisp PNG icons from SVG
for size in 16 32 48 150 180 192 512; do
  ICON_OUT=$ICON_FILE-${size}.png
  DIMENSIONS=${size}x${size}
  rsvg-convert -w "$size" -p 300 -d 300 "$ICON_PATH" > "$FAVICON_PATH/$ICON_OUT"
  printf "Created $FAVICON_PATH/$ICON_OUT\n"
  # Use ImageMagick to center the image and make it square
  magick "$FAVICON_PATH/$ICON_OUT" -gravity center -background transparent -resize "$DIMENSIONS" -extent "$DIMENSIONS" "$FAVICON_PATH/temp-$ICON_OUT"
  printf "Resized $FAVICON_PATH/temp-$ICON_OUT\n"
  # Use pngquant to reduce the size of the PNG
  pngquant 256 < "$FAVICON_PATH/temp-$ICON_OUT" > "$FAVICON_PATH/$FAVICON_FILE-$DIMENSIONS.png"
  printf "Quantized $FAVICON_PATH/$FAVICON_FILE-$DIMENSIONS.png\n"
done

# Merge the 16, 32 and 48 pixel versions into a multi-sized ICO file
magick \
  $FAVICON_PATH/$FAVICON_FILE-16x16.png \
  $FAVICON_PATH/$FAVICON_FILE-32x32.png \
  $FAVICON_PATH/$FAVICON_FILE-48x48.png \
  -background transparent \
  $FAVICON_PATH/$FAVICON_FILE.ico
printf "Created $FAVICON_PATH/$FAVICON_FILE.ico\n"

# Create Apple touch icons
mv $FAVICON_PATH/$FAVICON_FILE-180x180.png $FAVICON_PATH/apple-touch-icon.png
printf "Created $FAVICON_PATH/apple-touch-icon.png\n"

# Create Android Chrome icons
mv $FAVICON_PATH/$FAVICON_FILE-192x192.png $FAVICON_PATH/android-chrome-192x192.png
printf "Created $FAVICON_PATH/android-chrome-192x192.png\n"
mv $FAVICON_PATH/$FAVICON_FILE-512x512.png $FAVICON_PATH/android-chrome-512x512.png
printf "Created $FAVICON_PATH/android-chrome-512x512.png\n"

# Create MS tile icon
mv $FAVICON_PATH/$FAVICON_FILE-150x150.png $FAVICON_PATH/mstile-150x150.png
printf "Created $FAVICON_PATH/mstile-150x150.png\n"

# Clean up temporary files
rm $FAVICON_PATH/temp-*.png
rm $FAVICON_PATH/$FAVICON_FILE-48x48.png
rm $FAVICON_PATH/$ICON_FILE-*.png
printf "Cleaned up temporary files\n"

# Create Browserconfig file
cat > $FAVICON_PATH/browserconfig.xml <<EOF
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            <square150x150logo src="/mstile-150x150.png"/>
            <TileColor>#ffffff</TileColor>
        </tile>
    </msapplication>
</browserconfig>
EOF

# Create manifest file
cat > $FAVICON_PATH/site.webmanifest <<EOF
{
  "name": "",
  "short_name": "",
  "icons": [
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display": "standalone"
}

The ready to copy version is available on the gist: https://gist.github.com/mausic/svg-to-png-icons

Note

As you see I added a few lines, to generate browserconfig.xml and site.webmanifest files as well.

Maybe you will think, that this is overkill, and regenerating these files every time you generate icons is not necessary. But I like to have everything in one place.

Make the script executable:

chmod +x generate-icons.sh

And run it inside your project directory:

./generate-icons.sh public/logo.svg public

Good luck with your project! 🚀

Links