When I redid my site to use Hugo I also redid my favicon. I wanted this favicon to also serve as the Add to Home Screen functionality provided by Chrome. I expected Chrome to automatically scale up the favicon. When I designed it I made it pixelated on purpose, so some very crude scaling was just what I wanted. Unfortunately Chrome doesn’t scale automatically, so my add to home screen icon was just a blue square with a white S set in the center.
It turns out these are called Touch Icons, and they’re more complicated than I
expected. This blog post does a
good job with an overview. Basically, Chrome uses a conventional
rel="icon" sizes="192x192x" href="/touch-icon-192x192.png"> syntax and Apple
uses a number of
rel="apple-touch-icon-precomposed" with different
I wanted a simple command to scale up my favicon, keeping it pixelated. All the tools I could find to scale up an icon tried to smooth it out and ended up making it look trippy. First I used Preview to export the favicon as a PNG so that tools would have an easier time interacting with it.
convert -resize 192x192 favicon.png favicon_imagemagick.png
scalex -k 4 favicon.png favicon_scalex.png
Note that because
scalexonly permits certain scaling factors, the inline image above is further scaled to 192x192 using the technique described below. The original output of the command had dimensions of 16 * 4 = 64, and can be seen here.
Can you do this with something like Inkscape or Preview? Probably, but it’s a lot of annoying dragging and dropping.
Despite my best efforts, I couldn’t find a simple “scale and keep it looking
pixelated” solution online. It must exist, possibly even as an ImageMagick
filter, but that documentation is confusing. It was easy enough to make my own.
All I needed to do was access the pixels of the PNG and amplify each one by my
scaling factor, making it essentially take up a new square pseudo-pixel in the
image. This new pseudo-pixel would be a square of
factor true pixels on each
To access PNG byte’s I used PyPNG.
I wasn’t able to install it globally for some reason, so I used
mkdir virtualenv_png cd virtualenv_png virtualenv . source bin/activate pip install pypng
Now you can
import png. Don’t forget that to exit the virtual environment and
get back to regular Python you need to type
This script loads the PNG and amplifies each pixel as described above. The result is a perfectly scaled, pixelated icon.
# -*- coding: utf-8 -*- """ Scale a PNG by an integer factor, keeping it pixelated. """ import png from sys import argv # We're assuming rgba for for ints per pixel. Looking at the pypng docs there # are other options this might be, but this is all I needed. VAL_PER_PIXEL = 4 # rgba if __name__ == '__main__': # we expect scale_factor, in_file, out_file if len(argv) != 4: raise ValueError('Usage: scale_factor, in_file, out_file') factor = int(argv) in_file = argv out_file = argv reader = png.Reader(in_file) content = reader.asDirect() rows = content # content assumes at least one row. There are original_width pixels in # this row, and each pixel has VAL_PER_PIXEL ints for each pixel. original_width = len(content) / VAL_PER_PIXEL original_height = len(content) out_width = original_width * factor out_height = original_height * factor all_rows =  # Iterate over each row. Each pixel is represented by several ints (the # exact number depends on the file and is defined by VAL_PER_PIXEL). Each # pixel must be grown by factor, and each row must then be appended factor # number of times to all_rows. for row in rows: new_row =  for num_pixel in range(len(row) / VAL_PER_PIXEL): # / 4 for rgba # if a single pixel row was [255, 128, 25, 0], we would want to # expand that times 4. pixel_start = num_pixel * VAL_PER_PIXEL pixel_values = row[pixel_start:pixel_start + VAL_PER_PIXEL] larger_values = list(pixel_values) * factor new_row = new_row + larger_values for i in range(factor): # we've enlarged the row, now add it. all_rows.append(new_row) with open(out_file, 'w') as f: # alpha = True for rgba. This will have to be updated for PNGs without # alpha. w = png.Writer(out_width, out_height, alpha=True) w.write(f, all_rows)
With this script, we can take the 16x16 favicon png and scale it up by a factor
of 12. First copy or download the script and save it
virtualenv directory that has
pypng installed, then run it. In this
case, we’ve saved the scrip as
scale.py. Here is the same script in a
that I’ve only tested this with my single favicon, but for that it works
just fine. PNGs in general, non-square PNGs, etc – no promises.
python scale.py 12 favicon.png favicon_scaled.png
And there we have it:
Now that we have the largest size and we know it’s perfect, we can use ImageMagick to convert to the other sizes. 120x120, or instance, is:
convert -resize 120x120 favicon_scaled.png favicon_120.png
Then add the
<link> tags to your
<head> and be happy they’re so pretty.