This Lossless PNG Optimization You’re Probably Not Using Shrunk One File an Extra 39%
At Zoompf we routinely get asked questions about the best way to optimize images for performance. The other day, however, we received one question that left us scratching our heads. After some detective work, we uncovered a little known, yet extremely effective tactic for lossless PNG reduction that you very likely aren’t using (but should).
To begin, let me paraphrase the original question, with only slight embellishment protect the identities of the innocent…and help us feel better about ourselves:
Your free report is awesome!…Awesome! Awesome! Awesome!
One question, though – it said I could reduce the size of my image “red.png” from 283 kb to 173 kb using a lossless image reducing tool. I’ve tried a number of tools like smush.it and pngcrush, but at best I could only save a few kilobytes. How can I get that same level of savings shown in your report?
P.S. You’re awesome!
Okay, I maybe added the “awesome” parts, but I’m still pretty sure it was from Batman.
To start our journey, I am first making the assumption you are familiar with, at least at a high level, the concept of lossless image reduction (reducing the size of your image with NO loss in visual quality). If not, click on our help article right now for some pretty pictures, then come right on back.
So the image in original question was a PNG image used as a red gradient in the background. Quite common on the web:
The original size of the image was 283 kb. A quick upload to smush.it yielded a pretty unimpressive reduction of 4.3 kb down to 278 kb. A far cry from the advertised 110 kb savings in the Zoompf report!
Clearly something was up, so we then used our favorite command line tool: pngcrush, running with the default options:
pngcrush red.png red_crushed.png
12.3 seconds later, the optimized image was only reduced to 280 kb (0.9%), worse even than smush.it!
Okay, this is looking suspicious. Was there a bug in our report? Did we miss something?
The Nuclear Option
To make sure, we then used the “nuclear” option in pngrush, also known as
-brute. Since images can vary wildly in content, there’s no single magic reduction algorithm that is always guaranteed to give you the largest size savings. When you use default pngcrush, it tries a few “best guesses” and then returns the best reduction. With
-brute, though, it goes all out and brute-force tries 148 different reduction algorithms looking for the single winner with the smallest result:
pngcrush -brute red.png red_brute.png
While smaller is always better,
-brute has one very significant drawback: it takes a LONG time to run. For example this example image took almost 2 minutes to process!
If your website has a lot of images,
-brute often isn’t worth the time cost, especially if those images change frequently. (More on this later). Still, it’s useful in situations like this where you want to find your best case scenario. So in this case, the
-brute optimized file was 28% smaller, or 204 kb. Now we’re getting somewhere!
But wait! Didn’t the Zoompf report say 173 kb? What gives…it’s still not small enough!
What Did We Miss?
By now, we were pointing fingers at ourselves. There must be a bug in our software! After diving into the code, the aha moment hit: we’re passing another option to pngcrush:
-reduce does is simple yet quite powerful: it counts the number of distinct colors used in your PNG image and reduces the Pixel Depth to the smallest size that will contain all those colors without sacrificing image quality (e.g. without removing any colors from the palette). So for example, if your image is using a 24 image bitdepth (“true color”, or over 16 million colors), but only actually uses 200 of those colors, there’s a LOT of wasted space in that file filled up by a bunch of extra 000s. Why not instead store as a PNG8?
-reduce does one other extremely valuable test: it examines the number of color “channels” used by your PNG (red, green, blue, alpha), where the alpha channel is used to register transparency in the image bits. If the alpha channel is not used anywhere in the image (e.g. no transparency), then there is no reason to store all those extra unused bits!
So returning to this example, we ran pngcrush again with the
-reduce option as well:
pngcrush -brute -reduce red.png red_reduced.png
The result: 39% reduction to final size of 173 kb, a full 110 kb smaller then the original “optimized” file of 283 kb. We found it!
So as we mentioned above, not all images are created alike, but this image took advantage of 2 very common patterns we see on the web: (1) it didn’t actually use all that many colors (quite common for PNG images, since PNGs work best for logos and clip art), and (2) it didn’t utilize transparency, one of the most common reasons people use PNGs in the first place. By stripping out the unused alpha/transparency channel, we were able to recognize significant additional savings (39%!) beyond a standard lossless optimization, all with no loss in visual quality! See for yourself in this final, optimized image (click the image to load the full size):
The More Later on “Brute”…
Now a quick return to the
-brute option: recall we had mentioned it took over 2 minutes to generate that file with
-brute. Clearly this is too long for most people, but there are some great compromises possible by playing around with other pngcrush options. Through the years we’ve come up with this convention for our scanners, which is lightning fast yet still yields great results (we’ll leave those other parameters for a future blog post, but you can feel free to safely use these without any impact on visual quality of your image):
pngcrush -rem alla -nofilecheck -reduce -m 7 (YOURFILE).png (OPTIMIZEDFILE).png
Obviously consult the pngcrush documentation for the options that work best for you, but the above parameters are safe for typical use cases and might be a good place for you to start.
Hooray! So happy ending, we’re all done, right? Nope, one more thing.
The Right Format for the Right Job
Recall above I mentioned the PNGs are best for logos and clipart with transparency. This example image is a gradient without transparency, though – more photographic in nature. This is the realm where JPEGs shine! By converting that original PNG to a JPEG at quality level 90 (which is quite high), the image reduces down to 51 kb in size, blowing away even the best of the PNG results above! Of course, converting to a JPEG is a lossy optimization, meaning there could be some reduction in visual quality (aka fuzziness), but at a quality level of 90 the results are usually too fine to detect with the human eye. See for yourself in this optimized JPEG version:
So while it’s great we found a new trick to squeeze more out of your PNG image sizes, always keep in mind the best optimization of all is knowing which image format is best for the job at hand.
If you want to see what image optimizations are possible for your site, check out our Zoompf Alerts, now available for free. Zoompf Alerts monitors your website for the common causes of slow performance, alerting you to specific code and configuration level changes introduced over time that slow down your website performance. Free to join and no installation required.