CSS Spriting Tips

Ryan Doherty

40

One of the most effective ways of speeding up a website’s render time is to reduce the number of HTTP requests required to fetch content. An effective method to achieve this is via CSS sprites, which is combining multiple images into one large image and using CSS to only display parts of it.

Here’s an example sprite:

Sprite Example

The purpose of this post is not to show how it makes a site faster, but cover some best practices when creating a CSS sprite.

Don’t wait until you are done slicing to begin spriting.

The problem with waiting until you’ve built the site is all your CSS and images have already been created. You will have to go back and re-write your CSS. You’ll also spend a ton of time in Photoshop attempting to fit 20-30 graphics into a sprite at once, which is extremely painful and tedious. It’s much easier to build the sprite step by step.

Arrange images opposite of how they will be displayed

This tip is a little confusing and I didn’t learn this until I was halfway through creating a large sprite. If an image is supposed to appear on the left of an element:

Sprite positioning example

Put that image to the right of the sprite (see sprite image from above). This way when you move the background image via CSS, there is no possible way any other image will display next to it. A common problem when creating CSS sprites is positioning images so they don’t appear as part of a background for the wrong element.

Avoid ‘bottom’ or ‘right’ in CSS when positioning

When positioning a CSS sprite behind an element, it’s very easy to use background-position: bottom -300px; or background-position: right -200px;. This will work initially, but the problem with it is once you expand your sprite in width or height, your positioning will be wrong as the images are no longer at the bottom or right of the sprite. Using explicit positioning in pixels avoids this issue.

Give each image plenty of space

As you can see from the example sprite, many tiny images are given lots of space. Why not just cram them all together to make the sprite smaller? Because the elements they are used on will most likely have variable content and need that extra padding so other images don’t show up.

Here’s an example:
Variable content example

Each list item has a graphical number as a background image. If you look at the sprite shown above, you can see how the images were staggered so that if the content increased, no other images would show up.

Don’t worry about sprite pixel size

Chances are if your site is decently designed, you will have a lot of images to put in your sprite. And you’ll need a pretty large sprite to space the images out appropriately. And that’s fine. Empty space in a sprite does not take up much of a file’s size. The sprite used for addons.mozilla.org is 1,000×2,000 pixels and is only 16.7kb!

Got other tips? Leave a comment!

More Resources

40 responses

  1. NM wrote on ::

    What about the RAM usage? a 1000×2000 pixel image will take up 6MB while it’s displayed.

  2. rdoherty wrote on :

    @NM

    I’m not sure about the RAM usage. How much memory would be taken up by the 15 or more images if they were loaded separately? I’m not sure about overhead, but it might be similar.

  3. Dorus wrote on :

    Important tip on the file size: Save the image as .png, and not as .gif and absolute NOT as .jpg. .png is very efficient in big images with big squares of the same colour, for example all the white.

  4. Bill wrote on :

    While we’re mentioning pngs, if you need transparency in your sprite but are worried about IE6 looking fugly, you can use this script: http://www.dillerdesign.com/experiment/DD_belatedPNG/

    I’m using it in a production site for one of my clients and it works great.

  5. Stuart Parmenter wrote on ::

    Aieeeeeee! I don’t think using these huge images is good advise!

    NM is almost right. That image is going to take up 7.6MB of memory (1000*2000*4 bytes) whereas storing each image separately will take up substantially less memory. You’ve probably just doubled the amount of memory your website uses!

    If you do want to store images together, I would suggest only storing images that of the same width and vertically stacking them. This will allow the whole image to be copied in one pass rather than having to copy the image row-by-row.

    I think you’re much better off using individual images for this stuff as far as page memory and performance goes. If it slows down page loads too much, then it sounds like a way of specifying exactly what part of an image is used is needed so that you don’t waste megabytes of space with nothingness.

  6. Vlad Vukicevic wrote on ::

    It’s not fine! This is a horrible idea — the vast majority of that image is completely unused, so it takes up a huge amount of memory when decoded, regardless of how well it compresses on disk. The only thing you save by doing this is some network requests; you don’t even save much on the overall image compression, since you can probably get better compression by just having each piece in a separate file given the size of the individual pieces. Not to mention the added complexity of using such an image — you have to remember/look up exactly where in your image the particular piece you want to use is, and set background offsets correctly; this is much more complex than just referencing a well-named png.

    It’s fine to put together images that are the same size in a sprite — say, an image composed of a bunch of 32×32 icons. It’s not at all a good idea to put together completely unrelated elements into one big image. It saves you very little and significantly increases the memory and processing requirements when it’s drawn. Please avoid using this technique, and avoid suggesting it to others :)

  7. Mark Caudill wrote on ::

    Dorus: Using a PNG is good, but you must have GIF fallback for IE6- (as you can’t use AlphaimageLoader with background-position).

    Be sure to use background-repeat when using CSS sprites.

    I would also avoid having *too* much blank space in an image sprite. I normally stick to putting related images in a sprite and avoid excessive spacing.

    Another trick not mentioned which is really useful is having an entire row (since sprites consist of columns and rows) of the image be a repeatable pattern, and then just using repeat-x.
    Ex: http://buzbe.com/images/layout/menu.png

    Anyway, great post!

  8. Vlad Vukicevic wrote on ::

    I should also mention — images will generally take up width * height * 4 memory. So that 1000×2000 image on AMO, with most of it completely unused, takes up ~8MB of memory! Much of that design can be done using border-radius, and eventually, using CSS gradients. For now you’d still need tile gradient images… but a note about that as well — tiling 1px wide or 1px tall images is slow; make those images bigger! Tiling a gradient horizontally with the tile being 32 or 64 pixels is going to be much, much faster than tiling a 1px-wide image, especially if the gradient is tall.

  9. morgamic wrote on ::

    That’s a lot of good feedback. Ryan – maybe a follow-up post talking more about when it’s appropriate to use sprites or not and some comments on memory would make sense. I think most web devs are on a quest to improve ‘perceived’ performance, and system-wide memory usage probably doesn’t factor in when they are just hitting f5 on a single tab, so this is an important discussion we’re having.

    If there is a strong argument against using one large image we could also just boil this down to some tests using the personas sprite as an example. Some questions I’d have:

    – What additional overhead would it cost us to have N images instead of 1?
    – Why is memory usage so much smaller that way? the total memory usage of individual images isn’t calculated using h * w * 4?
    – How does loading multiple images affect perceived performance? If you have 20 images (even simple ones) it takes a toll on load time, and that’s an important factor. Thing about memory is that it’s fast, but also finite. So we should have discussion on the trade-off there.

  10. morgamic wrote on ::

    Nevermind, sum of smaller images in memory is much smaller than a big image. Guess it boils down to all the wasted memory storing the whitespace. :(

  11. Jim Plush wrote on ::

    Just did a quick memory test compared to other sites and addons seems to use an enormous amount of memory.

    check the screen cast here: http://litfuel.net/temp/addons.mov

    I opened a fresh browser, went to yahoo.com refreshed 10 times, memory footprint slightly increased then settled back down. I went then to the addons site and the memory shot from 100mb to way past 600MB, it does settle back down to around 300mb or so but that seems to be way higher than the handful of sites I compared addons too.

    Just loading the sprite graphic in a fresh browser consumed 10MB of memory.

    Just loading addons site once increases memory 200MB

    might be worth investigating.

  12. morgamic wrote on ::

    Seems like on a lot of pages I can easily have it jump 15M.

  13. Jim Plush wrote on ::

    In my case I have caching turned off in the browser. Most devs I’ve ever worked with also always disable all caching. With caching off you’re making around 30 GET requests to the same sprite element so 10mb x 30 requests == almost 300MB of sprites in memory, at least thats what appears to be happening by looking at the firebug net tab on addons.

    We toyed around with sprites here but in the end there were just too many downsides for our specific purposes. Of course with the traffic you guys get I’m sure every byte of transfer saved counts.

    Firefox 3’s memory management seems to be light years better than previous versions though. I submitted bugs against FF in the past since it used to hold memory to the largest amount it grew to. Example if you used 600MB of ram, you’d be no lower than 500 MB for the rest of the browsing session. Which meant we had to keep small memory objects in javascript since our app was 30,000 up the air and we had no access to it.

  14. Arun J wrote on ::

    Mark Caudill: IE6 does support 8-bit PNG though. So unless you’re using alpha transparency, the fallback option can be a 8-bit PNG instead of GIF.

    morgamic: Let’s say one has a site that uses many different icons, background gradients, button backgrounds, etc. So instead of going crazy with a huge sprite, one could group related images and create a small sprite for each group. That way server requests are cut down and (I’m guessing) memory will be managed more effectively. Would that be a good idea?

    On another note, even if you use sprites, a server request is still sent every time you refer it in different CSS selectors. It would return a 304 but that still counts as a GET request. So to actually cut down on server requests you should be referring the sprite in one CSS selector only and using other selectors only to change its background-position property.

  15. Arun wrote on ::

    Question: what are the effects of large image sprites on mobile web browsers? (Opera Mini for instance) Disastrous?

  16. morgamic wrote on ::

    Ideally you wouldn’t do it (even for the small grouping sprit approach) for mobile by switching out the screen media type with “handheld” but not all browsers on mobile devices support this, and it depends on how important your mobile audience is to your business development.

    If it was important for your site to work well for mobile, you’d avoid most images altogether and try to do as much as you can with CSS-only and minimal HTML. If you use standards and degrade gracefully (using @import for a lot of CSS) you may already have won this battle.

  17. Maurice wrote on ::

    Very large sprites take a lot of memory and are slower to display. The problem becomes more noticeable when elements with large background images are being dragged’n’dropped. It’s even worse with older browsers such as IE6.

    My advice would be to group related sprites only in one single image. You might be going to far when you have “one image to rule them all”. But there’s no absolute rule here, just a matter of balance.

  18. IS wrote on :

    You should check out SmartSprites:
    http://smartsprites.osinski.name/
    A very useful tool for semi-automatically generating sprites+CSS from your existing images and CSS.

  19. Mark Caudill wrote on ::

    Arun J: That reminded me of using 8-bit Fireworks PNGs for IE5/6 failover support (different from regular 8-bit PNGs)… It’s actually a pretty handy format that can be perfect given the right circumstances.
    See: http://www.sitepoint.com/blogs/2007/09/18/png8-the-clear-winner/

    Also, I normally pile all similar images into one sprite, then have one selector that specifies that background-image. From there, selectors for each individual part can specify width/height/background-position/repeat. That’s the most ideal way that I’ve found.

  20. rdoherty wrote on :

    Couple of counter-points:

    1) AMO’s sprite is way above average in size. IMO 8MB of memory for 1 PNG isn’t the end of the world considering how rich plenty of other websites are in their use of images, Flash and JavaScript.

    2) @Jim Plush: I can’t reproduce your memory tests. Visiting yahoo.com actually used more memory than AMO for me. (~90MB for Yahoo, ~83 for AMO). Repeated tests yielded the same results. Your memory issue must have been due to your caching settings or a bad add-on.

    3) I do agree on grouping sprites based on functionality or areas of a website. It’s not a bad idea to do this for bandwidth if users may not see certain areas of your site. And for ease of development.

    4) @Arun: When far-future expires headers are added to images, sprites are only requested 1 time for a page load, not every time the image is referenced in a CSS file.

    Spriting is very common, an industry-standard practice and a huge performance boost for page load times.

    More info:
    http://developer.yahoo.com/performance/
    http://www.ryandoherty.net/2008/10/12/optimizing-openspacebook/ (step 1 shows the speed boost in reducing http requests)
    http://spritegen.website-performance.org/section/what-are-css-sprites

  21. Boris wrote on :

    > the total memory usage of individual images isn’t calculated using h * w * 4?

    morgamic, it is. But you have a bunch of blank space in your image.

    As a simple example, if your image only uses 10×10 px sprites and leaves 2px padding around each one (and you’re advocating leaving more) and actually packs them efficiently (so no large blank areas), then the total number of pixels per sprite will be 196 (14 * 14). The number of pixels in the sprite itself is 100. So you’re using twice as much memory right there, and this is a much less wasteful scenario than the AMO example.

  22. morgamic wrote on ::

    @Boris – Yeah, I followed up with my other comment once I thought about it:
    “Nevermind, sum of smaller images in memory is much smaller than a big image. Guess it boils down to all the wasted memory storing the whitespace.” Thanks, though. :)

  23. Fabrizio Calderan wrote on :

    @dorus#3
    You suggest using png images, but what about IE6? That browser need to use the alphaimageloader filter (and IE7 too) but, as we everybody know, that filter doesn’t allow to set a background-position… how can you change it? Or are you talking about an 8-bit png?

  24. Xarr wrote on :

    there is a script fixing ie6 png lack support: http://www.twinhelix.com/css/iepngfix/

    It looks like working.

  25. Xarr wrote on :

    I forgot to add that it supports background-position and background-repeat

  26. Dorus wrote on :

    As far as i remember IE 6 support transparent PNG’s just fine. It’s alpha transparency that doesn’t work, but that’s completely unavailable in gif in the first place. Also unneeded for the sprite.

    Also, rdoherty example is in png also, so i assumed it would be fine. Also, IE 6 is long gone from my system, so i couldn’t test anything.

  27. Wolf wrote on ::

    Agree with the points summed up here about memory usage. The example sprite has a lot of unnecessary whitespace. Take a look at how Yahoo!Health does it, this is a more effienct way (see http://tinyurl.com/dn6xfo ) . Group together related items, but try to avoid large images (e.g. the mentioned 1000x2000px images).

  28. Agent Scorpion wrote on :

    Google do it:

    http://www.google.co.uk/images/nav_logo4.png

    PNG images are compresed. GIF’s are compressed. You are using bitmap calculations which are NEVER used on the web.

    Ignorance should be a sin.

  29. Frato wrote on ::

    You can use YASC to easily generate the css rules for your sprite images : http://www.lecentre.net/fratoblog/yasc/

  30. Andreas wrote on :

    @Agent Scorpion: You ar wrong. A GIF/PNG/Foo/Bar will be handeld UNCOMPRESSED within the memory, it will be converted into a pixel bitemap. Therefore you MUST use bitmap calculation to get the RAM usage even the files are very small. And therfore: simply add as less whitespace as possible and everthing is fine.

  31. Joseph Hsu wrote on ::

    group sprites in seperate images based on usage, eg UI elements into one image and box corners in another, that way you make sure to use what you need.

  32. Gopal Raju wrote on ::

    Useful tips! will definitely implement for my site redesign!

    Gopal Raju
    productivedreams.com
    twitter.com/gopalraju

  33. Phil wrote on :

    Sprites are great when used in moderation. I tend to split my images into 3 files:
    1. fixed box size – Where the element within which the image is used, is a fixed size, these images can all be crammed together as there is no danger of them being incorrectly displayed with variable content.
    2. repeat-y images and vertically variable boxes.
    3. repeat-x images and (rare) horizontally variable boxes.

    Try it – it makes sense!

    And I leave you with a thought: What if all images could be g-zipped together on the server and unpacked by the browser? 1 http request, easy to manage, more control of what is cached and for how long. Images are also then held seperately in memory – if that is indeed beneficial? Would the processor time required to zip/unzip the files outweigh the improved download time though?

  34. art wrote on ::

    never used sprites for big elements like in example, only for small icons or navigation elements

  35. ksieke wrote on ::

    Tip for too large sprites: crush them without loosing quality ;)
    for png: pngcrush
    for gif: gifcicle

    fyi: If you use gif or png depends on your sprite-image. Test both, crush them, compare file size and quality.

    mfg, ksieke

  36. ksieke wrote on ::

    wups.. sry!
    @art: but you can also have advantages by using big images in sprites.. just think about reducing the number of requests ;)

  37. Fred wrote on :

    @Jim Plush

    You should absolutely use this on your site if you use CSS images, especially for tabs, menus and sprites to avoid image flickering effects.

    // Re-enable CSS image cache in IE6 SP1+
    if(document.all&&document.execCommand&&!window.opera)
    try{document.execCommand(“BackgroundImageCache”,false,true);} catch(e){}

    http://evil.che.lu/2006/09/25/no-more-ie6-background-flicker
    http://blogs.msdn.com/cwilso/archive/2006/11/07/ie-re-downloading-background-images.aspx
    http://msdn.microsoft.com/en-us/library/ms536419(VS.85).aspx

    As for the example above, I would use 3 CSS sprites:

    One wide sprite with: the 3 green and black and blue large width items
    One mid-size sprite with: all the 5x ~160px wide items
    One small sprite with: all the remaining ~25px and less circle items

    Grouping things of similar size together while giving items some space to “breath” makes total sense.

    Even with the examle above, moving the blue part top and the white part a little more more up would have save tons of unused space.

  38. Melissa Church wrote on ::

    I’ve been meaning to get to sprite-ing on my sites, but my problem is I spend way too much time on Word Press and to be honest I haven’t had much luck designing sprite elements that look any good in WP. I’m thinking I’m missing something when I dig into my themes and start messing with them. Maybe I’m not hooking the files up correctly on the backend?

  39. Steve-o wrote on :

    “And I leave you with a thought: What if all images could be g-zipped together on the server and unpacked by the browser? 1 http request, easy to manage, more control of what is cached and for how long.”

    mod_deflate…
    expires headers…
    cache control headers…
    etc…

    What do you mean “what if?”

  40. Social Engine Templates wrote on ::

    At first the sprite approach was very difficult for me because I’d spend so much time trying to calculate everything out… but after a while I got better at knowing how to place the images and now it works great for me! this post was awesome, thanks!