PNGCRUSH a Directory of Images

By  on  

One easy way of reducing website load time is by optimizing your images. PNG graphics are often more bloated than they need to be so using PNGCRUSH should be a no-brainer. PNGCRUSH's basic usage provides only single-file-crushage but I've created a script that crushes PNGs in directories recursively.

The Bash Script

#!/bin/sh
for png in `find $1 -name "*.png"`;
do
	echo "crushing $png"	
	pngcrush -brute "$png" temp.png
	mv -f temp.png $png
done;

Do whatever you can to compress your images so that your website will load as quickly as possible. PNGCRUSHing your images does not cause a loss of quality -- only a loss of excess file size! PNGCRUSHing my images saved over 120KB of bloated imagery for the recent redesign.

Recent Features

Incredible Demos

  • By
    MooTools TextOverlap Plugin

    Developers everywhere seem to be looking for different ways to make use of JavaScript libraries. Some creations are extremely practical, others aren't. This one may be more on the "aren't" side but used correctly, my TextOverlap plugin could add another interesting design element...

  • By
    Using Opacity to Show Focus with jQuery

    A few days back I debuted a sweet article that made use of MooTools JavaScript and opacity to show focus on a specified element. Here's how to accomplish that feat using jQuery. The jQuery JavaScript There you have it. Opacity is a very simple but effective...

Discussion

  1. heh, this post is so engrish: “so that your website load as quickly” and “does not cause a lose of quality”. mootools crew must be whipping you hard then :)

    pngcrush… was this something you found on your mac thingie? most inconsiderate of “normal” windows users :D

  2. Holy crap. In my defense this was a really late night post. Weak. My bad.

  3. Robbo

    Totally untested, but for windows users, as long as everything is in the one spot….

    Create a batch file called “crush.bat”
    Put the following code in it.

    @echo off
    for /f “tokens=*” %%x in (‘dir /b *.png’) do (
    echo “crushing %%x”
    pngcrush -brute “%%x” temp.png
    move /Y temp.png “%%x”
    )

    Then run it.

  4. Ben

    Is there a way to do this with PHP? Or something a bit more practical?

  5. @Ben: What’s impractical about this?

  6. @David Walsh: I don’t know how to run bash scripts :)

  7. Oh — in that case, try this:

    http://pornel.net/imageoptim/en

  8. rick

    It’s even easier than that, pngcrush supports * for selecting images to crush. Try: ‘pngcrush -d “/Users/rd/crushedimages/” *.png’ to crush ALL png images in the current directory and put the resulting files in “/Users/rd/crushedimages/” (change this to a directory of your choosing!

  9. jon wilson

    i think this was a great post. it has helped me much. thank you

  10. Very useful script!

  11. In terms of usability and performance, under windoes, it doesn’t get better than this for a gui – http://pnggauntlet.com

    a great feature is that you can simply drag/ drop images, and it will ‘smash’ them,a nd overwrite the original files on the fly.

    for example, pull your entire website to a local folder, search for *.png in win explorer, then simply drag/ drop the query results into pnggauntlet, making sure to check the ‘overwrite original files’ box, and let it smash down the file size.

    i just tested this workflow on a massive ecommerce website (about 2700 png’s worth about 40mb) and saved a whopping 3mb. of course, who knows how well the originals were optimized, but nonetheless, it’s a great result.

    another great feature is that it can optionally convert from jpg, tiff, gif and bmp to png.

  12. I was searching for some information on jpgcrush and google assumed I meant pngcrush… Anyway for anyone who is interested I have several tutorials on this subject (in python) one of which will make use of multiple processors (which saves quite a bit of time if you have a tonne of pngs.)

    http://www.ragingsloth.com/softwareEng/PNGOptimizerIndex.html

  13. Simplest way to do this.

    1. Create a folder for executables which will go into your PATH directory reference.
    2. Open folder containing PNGs you want to crush
    3. shift + right click in empty space in folder, “open command window in folder”
    4. type “pngcrush -d “crushed” *.png
    5. close command window and go to newly created ‘crushed’ folder, which now contains your optimised PNGs.

  14. Simplest way to do this.

    1. Make sure that pngcrush.exe is in a folder which is mentioned in the PATH directory reference, so it can be used from the command line.

    2. Open folder containing PNGs you want to crush.

    3. shift + right click in empty space in folder, “open command window in folder”

    4. type “pngcrush -d “crushed” *.png

    5. close command window and go to newly created ‘crushed’ folder, which now contains your optimised PNGs.

  15. files

    How to get it to abort if file size will be INCREASED?!

    | pngcrush 1.7.9
    | Copyright (C) 1998-2002,2006-2010 Glenn Randers-Pehrson
    | Copyright (C) 2005 Greg Roelofs
    | This is a free, open-source program. Permission is irrevocably
    | granted to everyone to use this version of pngcrush without
    | payment of any fee.
    | Executable name is pngcrush
    | It was built with libpng version 1.2.42, and is
    | running with libpng version 1.2.46 - July 9, 2011

    | Copyright (C) 1998-2004,2006-2010 Glenn Randers-Pehrson,
    | Copyright (C) 1996, 1997 Andreas Dilger,
    | Copyright (C) 1995, Guy Eric Schalnat, Group 42 Inc.,
    | and zlib version 1.2.3.3, Copyright (C) 1998-2002 (or later),
    | Jean-loup Gailly and Mark Adler.
    || Warning: versions are different between png.h and png.c
    || png.h version: 1.2.42
    || png.c version: 1.2.46

    | It was compiled with gcc version 4.4.4 and gas version 2.9.5(?).

    Recompressing wut.png
    Total length of data found in IDAT chunks = 548746
    color counting (-cc option) is not supported.
    Removed the pHYs chunk.
    unknown chunk handling done.
    IDAT length with method 11 (fm 0 zl 2 zs 2) = 2435942
    IDAT length with method 12 (fm 1 zl 2 zs 2) = 951323
    IDAT length with method 13 (fm 2 zl 2 zs 2) = 905037
    IDAT length with method 14 (fm 3 zl 2 zs 2) = 1016476
    IDAT length with method 15 (fm 4 zl 2 zs 2) = 858259
    IDAT length with method 16 (fm 5 zl 2 zs 2) = 846721
    IDAT length with method 17 (fm 0 zl 1 zs 0) = 697832
    IDAT length with method 18 (fm 1 zl 1 zs 0) = 797697
    IDAT length with method 19 (fm 2 zl 1 zs 0) = 755660
    IDAT length with method 20 (fm 3 zl 1 zs 0) = 859270
    IDAT length with method 21 (fm 4 zl 1 zs 0) = 761716
    IDAT length with method 22 (fm 5 zl 1 zs 0) = 753877
    IDAT length with method 23 (fm 0 zl 1 zs 1) = 697832
    IDAT length with method 24 (fm 1 zl 1 zs 1) = 797697
    IDAT length with method 25 (fm 2 zl 1 zs 1) = 755660
    IDAT length with method 26 (fm 3 zl 1 zs 1) = 859270
    IDAT length with method 27 (fm 4 zl 1 zs 1) = 761716
    IDAT length with method 28 (fm 5 zl 1 zs 1) = 753877
    IDAT length with method 29 (fm 0 zl 2 zs 0) = 673435
    IDAT length with method 30 (fm 1 zl 2 zs 0) = 765510
    IDAT length with method 31 (fm 2 zl 2 zs 0) = 731611
    IDAT length with method 32 (fm 3 zl 2 zs 0) = 835291
    IDAT length with method 33 (fm 4 zl 2 zs 0) = 735487
    IDAT length with method 34 (fm 5 zl 2 zs 0) = 729097
    IDAT length with method 35 (fm 0 zl 2 zs 1) = 673435
    IDAT length with method 36 (fm 1 zl 2 zs 1) = 765510
    IDAT length with method 37 (fm 2 zl 2 zs 1) = 731611
    IDAT length with method 38 (fm 3 zl 2 zs 1) = 835291
    IDAT length with method 39 (fm 4 zl 2 zs 1) = 735487
    IDAT length with method 40 (fm 5 zl 2 zs 1) = 729097
    IDAT length with method 41 (fm 0 zl 3 zs 0) = 654550
    IDAT length with method 42 (fm 1 zl 3 zs 0) = 710763
    IDAT length with method 43 (fm 2 zl 3 zs 0) = 694884
    IDAT length with method 44 (fm 3 zl 3 zs 0) = 791240
    IDAT length with method 45 (fm 4 zl 3 zs 0) = 694167
    IDAT length with method 46 (fm 5 zl 3 zs 0) = 692165
    IDAT length with method 47 (fm 0 zl 3 zs 1) = 654550
    IDAT length with method 48 (fm 1 zl 3 zs 1) = 710763
    IDAT length with method 49 (fm 2 zl 3 zs 1) = 694884
    IDAT length with method 50 (fm 3 zl 3 zs 1) = 791240
    IDAT length with method 51 (fm 4 zl 3 zs 1) = 694167
    IDAT length with method 52 (fm 5 zl 3 zs 1) = 692165
    IDAT length with method 53 (fm 0 zl 4 zs 0) = 587966
    IDAT length with method 54 (fm 1 zl 4 zs 0) = 695367
    IDAT length with method 55 (fm 2 zl 4 zs 0) = 672502
    IDAT length with method 56 (fm 3 zl 4 zs 0) = 771732
    IDAT length with method 57 (fm 4 zl 4 zs 0) = 675596
    IDAT length with method 58 (fm 5 zl 4 zs 0) = 670186
    IDAT length with method 59 (fm 0 zl 4 zs 1) = 634611
    IDAT length with method 60 (fm 1 zl 4 zs 1) = 698306
    IDAT length with method 61 (fm 2 zl 4 zs 1) = 688376
    IDAT length with method 62 (fm 3 zl 4 zs 1) = 785595
    IDAT length with method 63 (fm 4 zl 4 zs 1) = 680116
    IDAT length with method 64 (fm 5 zl 4 zs 1) = 674963
    IDAT length with method 65 (fm 0 zl 5 zs 0) = 567961
    IDAT length with method 66 (fm 1 zl 5 zs 0) = 659745
    IDAT length with method 67 (fm 2 zl 5 zs 0) = 651320
    IDAT length with method 68 (fm 3 zl 5 zs 0) = 745437
    IDAT length with method 69 (fm 4 zl 5 zs 0) = 648498
    IDAT length with method 70 (fm 5 zl 5 zs 0) = 645942
    IDAT length with method 71 (fm 0 zl 5 zs 1) = 615134
    IDAT length with method 72 (fm 1 zl 5 zs 1) = 667126
    IDAT length with method 73 (fm 2 zl 5 zs 1) = 669886
    IDAT length with method 74 (fm 3 zl 5 zs 1) = 762645
    IDAT length with method 75 (fm 4 zl 5 zs 1) = 658031
    IDAT length with method 76 (fm 5 zl 5 zs 1) = 655619
    IDAT length with method 77 (fm 0 zl 6 zs 0) = 560413
    IDAT length with method 78 (fm 1 zl 6 zs 0) = 618874
    IDAT length with method 79 (fm 2 zl 6 zs 0) = 623842
    IDAT length with method 80 (fm 3 zl 6 zs 0) = 710951
    IDAT length with method 81 (fm 4 zl 6 zs 0) = 618249
    IDAT length with method 82 (fm 5 zl 6 zs 0) = 619695
    IDAT length with method 83 (fm 0 zl 6 zs 1) = 606769
    IDAT length with method 84 (fm 1 zl 6 zs 1) = 627807
    IDAT length with method 85 (fm 2 zl 6 zs 1) = 643727
    IDAT length with method 86 (fm 3 zl 6 zs 1) = 730353
    IDAT length with method 87 (fm 4 zl 6 zs 1) = 629654
    IDAT length with method 88 (fm 5 zl 6 zs 1) = 630925
    IDAT length with method 89 (fm 0 zl 7 zs 0) = 558547
    IDAT length with method 90 (fm 1 zl 7 zs 0) = 605505
    IDAT length with method 91 (fm 2 zl 7 zs 0) = 612292
    IDAT length with method 92 (fm 3 zl 7 zs 0) = 695889
    IDAT length with method 93 (fm 4 zl 7 zs 0) = 608946
    IDAT length with method 94 (fm 5 zl 7 zs 0) = 610519
    IDAT length with method 95 (fm 0 zl 7 zs 1) = 604538
    IDAT length with method 96 (fm 1 zl 7 zs 1) = 614662
    IDAT length with method 97 (fm 2 zl 7 zs 1) = 631610
    IDAT length with method 98 (fm 3 zl 7 zs 1) = 714922
    IDAT length with method 99 (fm 4 zl 7 zs 1) = 620061
    IDAT length with method 100 (fm 5 zl 7 zs 1) = 621519
    IDAT length with method 101 (fm 0 zl 8 zs 0) = 555261
    IDAT length with method 102 (fm 1 zl 8 zs 0) = 589149
    IDAT length with method 103 (fm 2 zl 8 zs 0) = 597092
    IDAT length with method 104 (fm 3 zl 8 zs 0) = 676219
    IDAT length with method 105 (fm 4 zl 8 zs 0) = 596852
    IDAT length with method 106 (fm 5 zl 8 zs 0) = 598205
    IDAT length with method 107 (fm 0 zl 8 zs 1) = 600862
    IDAT length with method 108 (fm 1 zl 8 zs 1) = 597438
    IDAT length with method 109 (fm 2 zl 8 zs 1) = 614670
    IDAT length with method 110 (fm 3 zl 8 zs 1) = 693766
    IDAT length with method 111 (fm 4 zl 8 zs 1) = 606835
    IDAT length with method 112 (fm 5 zl 8 zs 1) = 607756
    IDAT length with method 113 (fm 0 zl 9 zs 0) = 554346
    IDAT length with method 114 (fm 1 zl 9 zs 0) = 578629
    IDAT length with method 115 (fm 2 zl 9 zs 0) = 587438
    IDAT length with method 116 (fm 3 zl 9 zs 0) = 665646
    IDAT length with method 117 (fm 4 zl 9 zs 0) = 586698
    IDAT length with method 118 (fm 5 zl 9 zs 0) = 588334
    IDAT length with method 119 (fm 0 zl 9 zs 1) = 599719
    IDAT length with method 120 (fm 1 zl 9 zs 1) = 586780
    IDAT length with method 121 (fm 2 zl 9 zs 1) = 603901
    IDAT length with method 122 (fm 3 zl 9 zs 1) = 683089
    IDAT length with method 123 (fm 4 zl 9 zs 1) = 595879
    IDAT length with method 124 (fm 5 zl 9 zs 1) = 597310
    IDAT length with method 125 (fm 0 zl 1 zs 3) = 2246417
    IDAT length with method 126 (fm 1 zl 1 zs 3) = 876458
    IDAT length with method 127 (fm 2 zl 1 zs 3) = 801576
    IDAT length with method 128 (fm 3 zl 1 zs 3) = 940918
    IDAT length with method 129 (fm 4 zl 1 zs 3) = 753268
    IDAT length with method 130 (fm 5 zl 1 zs 3) = 741504
    IDAT length with method 131 (fm 0 zl 4 zs 3) = 2245807
    IDAT length with method 132 (fm 1 zl 4 zs 3) = 876189
    IDAT length with method 133 (fm 2 zl 4 zs 3) = 801449
    IDAT length with method 134 (fm 3 zl 4 zs 3) = 940635
    IDAT length with method 135 (fm 4 zl 4 zs 3) = 753015
    IDAT length with method 136 (fm 5 zl 4 zs 3) = 741282
    Best pngcrush method = 113 (fm 0 zl 9 zs 0) for temp.png
    (1.02% IDAT increase)
    (1.02% filesize increase)

    CPU time used = 47.830 seconds (decoding 2.470,
    encoding 45.050, other 0.310 seconds)

  16. files

    4 months and no-one’s answered me… This problem really annoys me. It seems that pngcrush’s missing some packing methods…

  17. Useful script! Thank you. I’d suggest changing

    for png in find $1 -name "*.png";

    to

    for png in find $1 -iname "*.png";

    in order to catch files with extensions such as .PNG and .Png.

    • Everyone steals from me :(

    • Hey now Thomas, that’s a bit of a dick allegation! And a huge let down after I saw http://davidwalsh.name in a list of my site’s backlinks! If I’d found David’s post I would gladly have used his tools rather than spend a few minutes writing my own which I then posted to my site to save it should I need it again. But, you know, if a famous guy said a similar thing before me and I didn’t see or hear it, then I “must” be stealing it. Ugh.

      The two scripts aim to achieve the exact same goal using the same most obvious choice tools (find, do while, mv), and use the same most obvious variable names – ‘temp’ for the temp file and ‘png’ for the singular entry in the list of pngs. If you tasked 100 competent developers with this, 95 of them would have a very similar script, and the other 5 scripts wouldn’t work.

      I’ll take it as the best compliment that my shitty little bash script can be confused as coming from *the* David Walsh, even if mine has slightly better parameter handling :p

  18. Just a suggestion, but it probably makes sense to nice this if running on a live server. Additionally, you can speed this up *a lot* on a multicore box by passing this through xargs.

  19. sloppp

    says “illegal option n” or something like dat

    this works for me

    pngcrush -d crushed_png *.png
    cp crushed_png/*.png .
    rm -rf crushed_png
    
  20. joe

    UBUNTU / DEBIAN tested

    Script for Bash – command

    Place in your .bashrc of the user
    be carefully : done by users rights / group rights; so by doing that with root,
    output file is owned by root/root

    so it makes sense to write chkpng in root/.bashrc
    and
    write fixpng only in /home/user/.bashrc

    function fixpng() {
        for png in find $1 -name "*.png";
            do
                echo "crushing $png"
                pngcrush -rem sbit "$png" tmp.png
                mv -f tmp.png $png
            done;
    }
    
    
    function chkpng() {
        for png in find $1 -name "*.png";
            do
            #    echo "checking $png"
                pngcheck -q "$png"
            done;
    }
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!