PNG is a popular format for icons, sprites, logos, screenshots, and many others. Creating PNG images is fairly easy, but what do we need to do if we want to get the smallest file possible without any perceptible loss of quality?

In this context, we define an image as a two-dimensional array of color values, where every color is represented with its red, green, blue, and alpha component. If each color component is a byte (8 bits), then every individual color will consume 4 bytes. A 100×100 image thus requires the space of 40,000 bytes. PNG format can reduce the storage consumption by identifying a group of similar colors and encoding them using shorthand codes. This is, of course, well-known as a form of data compression. Assuming there are repeated patterns here and there, a file representing 100×100 image in PNG format will likely have the size less than 40,000 bytes.

Color quantization

At the end of the day, the colors in the image will appear on a screen in a form of pixels. As your user is looking at the screen (and thereby also the image), do you think they thoroughly inspect the pixel one by one, looking for a missing color? If that is not the case, then the first question that you shall ask yourself is, do you need every single possible color in that image? If the answer to that question is yes, then skip to the next section. But if the answer is no, then you can evaluate whether you would sacrifice a few colors to save some significant space.

Fortunately, there is an easy way to do that: quantizing the colors. Instead of all 16 millions of RGB combinations, you can restrict the image to have a certain maximum number of distinct colors. A fantastic tool that can do that is pngquant from @kornelski. Once it is installed (on macOS with Homebrew: brew install pngquant, Nix users: nix-env -I pngquant, with Debian/Ubuntu: apt-get install pnquant), it is rather straightforward to use. Here is an example:

pngquant 64 --speed 1 image.png

The above command quantizes the color in image.png into just 64 colors. The option --speed 1 is used to favor the best quantization even if the process is slightly slower (by two seconds at most). The output, the image with the quantized colors, is called image-fs8.png. Of course, since colors are sacrificed in the name of storage, this type of optimization is rather lossy, i.e. the quantized version is slightly different (but hopefully not noticeable) from the original.

Let us assume that you write an article on leadership and management and that you have to include a simple organization chart as follows:

orgchart

Saved as PNG, the above image (400×200 pixels) results in a file with the size of 6,664 bytes.

There are 782 distinct colors in the image. If you choose to quantize the image to only up to 64 colors, it will look like this:

orgchart-fs8

Meanwhile, the file size decreases dramatically to only 2,952 bytes. That is even less than half of the original!

(Note from the editor: 2,736 bytes actually, after running through ImageOptim out of habit)

An avid Photoshop user might compare both images by placing them in layers and then look for color differences. For the rest of us, the quantized version of the organization chart is visually just as good as the original one.

Imagine how many bytes do not need to travel over the network if your article becomes very popular. The saving could be significant!

Better compression

Regardless whether you choose to quantize the colors or not, there is a highly recommended step before distributing PNG images to the world: use a better compression. Unlike the previous step, this optimization is completely lossless, i.e. the output has the same exact colors as the original.

A perfect tool to do this Zopfli, a better compressor for Google that is still compatible with DEFLATE, a compression method used by PNG. Just like pngquant, Zopfli is also relatively easy to obtain (on macOS with Homebrew: brew install zopfli, with Debian/Ubuntu: apt-get install zopfli, Nix users: nix-env -I zopfli).

Using Zopfli is amazingly easy and fun:

zopflipng input.png output.png

With the previous example, the quantized organization chart image, Zopfli still manages to shrink the PNG file even further, from 2,952 bytes to 2,625 bytes. Granted, the saving is not much, but since it practically costs almost nothing to run Zopfli, there is no reason to skip it!

Please be advised that Zopfli currently removes various metadata from the PNG image. Thus, if your image has a color profile, it may be gone after going through Zopfli and that may have an impact on the visual representation. Do not forget to compare the output vs the original and exercise your judgement.

Your mileage may vary, but there is no harm in trying out pngquant and zopfli. Even better, if you know these tools are working out for you, do not forget to incorporate then as part of your build pipeline in the continuous integration/deployment system. This is a simple step on your end and yet it can benefit every single visitor to your web site.

Let us save the planet, one PNG image at a time.

ABOUT THE AUTHOR

Ariya Hidayat

Ariya Hidayat (@ariyahidayat) is an engineer/technical lead/FOSS fan/blogger/speaker. These days, his activities are mostly on software craftsmanship around HTML5, JavaScript, and other web technologies.

3 Responses to “Squeezing PNG Images”

  1. Jacob G

    I’d recommend Pingo: http://css-ig.net/pingo – on the site there are benchmarks too.

  2. Nick Doyle

    Or use lossless WebP/JPEG 2000/JPEG XR. These will generally outperform all of these PNG tricks. Their lossy forms will generally greatly outperform lossy colour quantization in weight and perception too when measured with SSIM.

  3. WebPerformance notes from PerfPlanet – Technology 2.0

    […] 26: PNG Image optimizations PNG optimization using pngquant and […]