Command-line CSS spriting

February 19th, 2011. Tagged: CSS, images, smush.it

(In Russian)

OK, CSS sprite tools exist. I'm pretty confident I actually made the very first one :) But they break from time to time (like mine currently). And then the command line is cool (as opposed to scary) and oh-so-quick. And imagemagick is cool and oh-so-powerful. So let's see how we can create CSS sprites from the command line alone.

Creating the image

Starting with a list of separate image files:

$ ls 
1.png  2.gif  dot.png  phoney.gif  tw.gif
  • - 1.png
  • - 2.gif
  • - dot.png
  • - phoney.gif
  • - tw.gif

Creating the sprite:

$ convert *png *gif -append result/result-sprite.png

Yes, that's all! The result:

What?

So the imagemagick command is generally something like:

$ convert image1.png image2.png image3.png -append result/result-sprite.png

But we can also replace the list of images with *s:

$ convert * -append result-sprite.png

Or as in the previous case, limiting to *.gif and *.png.

How about a horizontal sprite? All it takes is changing -append to +append:

$ convert *png *gif +append result/result-sprite-horizon.png

The result:

Also note how the source images can be any format - GIF, PNG, JPEG and the result is PNG. Actually I'd recommend always trying PNG8 first:

$ convert *png *gif -append PNG8:result/result-sprite-horizon.png

CSS positions

Now since this is all hand-made there's no auto-generation of CSS. But it's still pretty straightforward. Take the vertical sprite:

All images will have background-position-x of 0px, so that's easy.

The first image will also have Y-position 0px. It also happens to be 16x16 pixels. So it's:

.first {
  width: 16px;
  height: 16px;
  background: url(result/result-sprite.png) 0 0;
}

... where 0 0 position is redundant and can be omitted.

The second image is also 16x16, that's convenient. Its X is 0 and its Y is the height of the previous image (16px) with a minus in front. So:

.secondo {
  width: 16px;
  height: 16px;
  background: url(result/result-sprite.png) 0 -16px;
}

And so on. Y position of an image is Y of the previous + the height of the previous.

You can use the handy-dandy test page to play around with this (or any other) sprite.

But.. but... figuring out dimensions by keeping track of heights? You kiddin' me?

Imagemagick to the rescue. `identify` gives you the basic image info:

$ identify 1.png 
1.png PNG 16x16 16x16+0+0 DirectClass 8-bit 260b

`identify` also has a `-format` option and supports *. So getting all the info in a neat form is easy:

$ identify -format "%g - %f\n" *
16x16+0+0 - 1.png
16x16+0+0 - 2.gif
6x6+0+0 - dot.png
10x16+0+0 - phoney.gif
16x16+0+0 - tw.gif

%f is filename and %g is geometry.
\n is a new line as you would expect and sometimes - is just a -.
So if you want to figure out the Y position of the fifth element, well, it's the sum of the heights of the previous: 16+16+6+16

.last {
  width: 16px;
  height: 16px;
  background: url(result-sprite.png) 0 -54px
}

Some complicated math! 'scuse me while I ask my second grader if she can handle it :)

And some smushing

Imagemagick doesn't write optimal PNGs. So some optimization is due. You can do it yourself with pngout, optipng, etc. Or use web-based tools such as smush.it (you're welcome!) or punypng.com. (psst - how bout a glimpse of the past)

Or how about.... smush.it on the command line:

$ curl http://www.smushit.com/ysmush.it/ws.php
       ?img=http://www.phpied.com/files/sprt/result/result-sprite.png

Result is JSON:

{"src":"http:\/\/www.phpied.com\/files\/sprt\/result\/result-sprite.png",
 "src_size":1759,
 "dest":"http:\/\/smushit.zenfs.com\/results\/5a737623\/smush\/%2Ffiles%2Fsprt%2Fresult%2Fresult-sprite.png",
 "dest_size":1052,
 "percent":"40.19",
 "id":""}

Oh looky, almost half the filesize. Let me at it! Copy the `dest` URL:

$ curl http:\/\/smushit.zenfs.com\/results\/5a737623\/
       smush\/%2Ffiles%2Fsprt%2Fresult%2Fresult-sprite.png > result/smushed-sprite.png

And that's that.

Recap

  1. create image:
    $ convert *png *gif -append PNG8:result/result-sprite.png
  2. get dimensions:
    $ identify -format "%g - %f\n" *png *gif
    
  3. optimize:
    $ curl http://www.smushit.com/ysmush.it/ws.php?img=http://url...
    

Test page to play with the result-sprite is here.

For some more ideas and a different imagemagick command for generating sprites - see the very original post announcing the csssprites.com.

Tell your friends about this post: Facebook, Twitter, Google+

37 Responses

  1. [...] This post was mentioned on Twitter by Stoyan Stefanov, bad websites, m.y.ikegami_bot, Stueccles Flipboard, Jeffrey Macko and others. Jeffrey Macko said: blog: Command line CSS spriting http://t.co/SEUTV2l [...]

  2. This is so awesome I can barely keep my pants on!

    Thank you!

  3. The output of ls is not intended for other programs to parse, and there’s absolutely no reason to use it – the wildcards after ls are being expanded by your shell into a list of files, which ls is dutifully echoing back.

    $ convert `ls *png *gif` -append result/result-sprite.png

    Should just be:

    $ convert *png *gif -append result/result-sprite.png

    The expansion of `ls …` is happening before word splitting and glob expansion, so if any of your filenames happen to have spaces, asterisks, question marks, square brackets, etc. inside of them, you’ll get some weird results.

    See http://mywiki.wooledge.org/ParsingLs

  4. Wouldn’t it be better to ad an empty pixel between every sprite for the iDevice/Safari zoom problem where it takes a pixel to much when resizing?

  5. Thanks Evan, excellent point! Plus that will work on windows too.

    Updating the article…

  6. You can improve this to work like https://github.com/hagenburger/lemonade

  7. 1px gap between sprite parts is needed to work around zoom errors (at least in IE).

  8. SmartSprites (http://csssprites.org) take this idea further by parsing CSS files for background images, joining them into sprites and updating the CSS to reference the sprite images along with the right offsets. X- and Y-repeated images are supported too.

  9. As a side note…
    I think the best format for these five images is not horizontal nor vertical but square.

    xoo
    oo
    o

    That way there is no need to “sandbox” four of them as the ones at the end of each row can style the background of elements (top/left) without revealing other parts of the sprite.

    Sprites bring a lot regarding performance, but on the other hand, we’re often polluting documents with junk markup to accommodate them. Plus, background images don’t show on paper and disappear in Window’s optimization mode and high-contrast styles sheets.

    These show how to use sprites as img elements in the markup:
    http://www.artzstudio.com/2010/04/img-sprites-high-contrast/
    http://www.tjkdesign.com/articles/how-to_use_sprites_with_my_image_replacement_technique.asp

  10. [...] navegando, he leído en phpied, una forma mucho más rápida de hacerlo, usando el [...]

  11. Cool, I was just thinking about how to accelerate the process of creating sprites, and I happen to stumble with this tutorial. It seems like today is my lucky day.

  12. [...] http://www.phpied.com/command-line-css-spriting/ [...]

  13. Can you also add padding between each image? In the examples above, if you’d use the icons as bg. images for some text (list items, etc) and then zoom text only , there’ll be some image bleeding (not very bullet proof for a flexible design.)

  14. Here is a cross platform version which runs on
    - Windows, Linux, Mac
    - only dependency is a ~800kB executable from http://www.rebol.com/download-view.html
    - and it doesn’t require your second grader to calculate the offsets

    http://www.rebol.org/view-script.r?color=yes&script=css-sprite.r

    but for quick reference i can actually fit it here very well:

    REBOL []
    types: [%*.gif %*.png] sprite: %sprite.png spacing: 0×1
    name: func[f][ rejoin ["sprite-" head clear find copy f suffix? f]]
    files: remove-each f read %. [f = sprite or not any map-each t types [find/any f t]]
    montage: collect [ foreach f files [keep 'image keep f] ]
    save/png sprite to-image layout/tight compose [
    backdrop 255.255.255.255 space spacing pad 0x0 (montage)]
    offset: 0×0
    print map-each f files [
    i: load-image f class: name f
    rule: rejoin [
    "."class" {width: "i/size/x"px; height: "i/size/y"px; "
    "background: transparent url("sprite") 0 "offset/y"px no-repeat;}" lf
    ] offset: offset – i/size – spacing
    rule
    ]

    (note: i have some initial issues with transparency which i will iron out later)

  15. [...] a variety of reasons).As such, there’s always the command line option.Stoyan Stefanov has a great tutorial on using the command line and ImageMagick to create CSS sprites. His article covers:Creating the [...]

  16. I’ve used SmartSprites in the past, it works quite well and automates the building of the sprite, as well as the CSS changes required. It’s available at http://csssprites.org/

  17. Very nice tip. I always like small tips that can handle big job! And this is awesome for me.

  18. Yeah, tools are not as hard as they might seem – people just need to use them and plug them.

    BTW, my http://www.favoriteiconsofinternet.com/ is using almost the same process of “imagemagicking” for huge imagemap creation ;)

  19. Webdesign can be easy, if you use http://spriteme.org/

    SpriteMe
    finds background images
    groups images into sprites
    generates the sprite
    recomputes CSS background-positions
    injects the sprite into the current page

    It is really neat and you don’t need to install anything:-)

  20. I made a bash script much along the same lines which writes out the CSS – uses the same identify step etc just does it on a batch of files. More detail here: http://jaymz.eu/2010/05/building-css-sprites-with-bash-imagemagick/

  21. If you can understand Chinese, try http://www.99css.com/archives/726

  22. [...] library that uses CSS transforms and transitions to create smooth, hardware-accelerated animations.Command-line CSS spritingThe author shows, how to create CSS sprites from the command line alone.Last ClickCode StandardsThis [...]

  23. [...] Command-line CSS spritingThe author shows, how to create CSS sprites from the command line alone. [...]

  24. [...] Command-line CSS spritingThe author shows, how to create CSS sprites from the command line alone. [...]

  25. [...] Command-line CSS spritingThe author shows, how to create CSS sprites from the command line alone. [...]

  26. [...] Command-line CSS spritingThe author shows, how to create CSS sprites from the command line alone. [...]

  27. [...] Command-line CSS spriting The author shows, how to create CSS sprites from the command line alone. [...]

  28. [...] Command-line CSS spriting The author shows, how to create CSS sprites from the command line alone. [...]

  29. [...] Command-line CSS spriting The author shows, how to create CSS sprites from the command line alone. [...]

  30. [...] Command-line CSS spritingThe author shows, how to create CSS sprites from the command line alone. [...]

  31. [...] Command-line CSS spriting The author shows, how to create CSS sprites from the command line alone. [...]

  32. [...] Command-line CSS spriting The author shows, how to create CSS sprites from the command line alone. [...]

  33. [...] into it and kept going on manually creating them by online service for my projects. When I read an article about command line css spriting, I was amazed, how easy the map can be built using ImageMagick and [...]

  34. Try this tool
    http://compresspng.com
    You’ll be amazed, how much it can shrink after punypng and similar.

  35. [...] Command-line CSS spriting The author shows, how to create CSS sprites from the command line alone. [...]

  36. I had some problems getting transparency in my resulting sprite. I fixed this by setting a background color for the resulting images likewise:

    $ convert *.png -background “rgba(255,255,255,0)” -append ./sprite.png

  37. […] Command-line CSS spriting The author shows, how to create CSS sprites from the command line alone. […]

Leave a Reply