SpriteMe makes spriting easy

September 14, 2009 8:41 pm | 21 Comments

I’m excited to announce SpriteMe – a tool that makes it easy to create CSS sprites. It’s a bookmarklet, so it runs in Internet Explorer, Firefox, Safari, and Chrome. Just drag&drop this link to your bookmarks toolbar or right-click to add it to your favorites: SpriteMe.

A CSS sprite combines multiple background images into a single image. Spriting makes web pages faster because it reduces the number of HTTP requests required to load a web page. The problem is, creating sprites is hard, involving arcane knowledge and lots of trial and error. SpriteMe removes the hassles with the click of a button. Try the tutorial to see SpriteMe in action.

Features

Other sprite tools (like CSS Sprite Generator and CSS Sprites generator) are useful for combining multiple background images into a single sprite image. This is a big help, and I’ve frequently used these tools built by Stuart and Ed, and Stoyan. However, the bigger challenges when creating sprites are finding the background images and knowing how to group them into sprites. Once the sprite image is created, testing the sprite, including changing all the CSS, is time consuming. SpriteMe addresses all of these challenges. Here’s a rundown of SpriteMe’s features.

Finds Background Images: SpriteMe makes it easy to examine the CSS background images used in the current page by listing all the background images and the elements that use them. Hovering over its URL displays the background image. Clicking on one of the image’s DOM elements highlights that element in the page.

Figure 1. SpriteMe finds images and the elements that use them, and groups images into sprites.

Groups Images into Sprites: The logic behind spriting is complex: Background images that repeat horizontally can’t be sprited with images that repeat vertically. Repeating images that are sprited together must be the same width or height, depending on whether they repeat-x or repeat-y, respectively. Background images that are shorter than their containing block should go at the top of a vertically-stacked sprite if their position is “bottom”, but they should go at the bottom if their position is “top”. It’s complex – and this is just the beginning of the logic that’s required to avoid mistakes. SpriteMe contains this spriting logic and uses it to suggest which images to combine into a sprite. Figure 1 shows the varied width background images that SpriteMe recommends combining into a vertically-stacked sprite for eBay.

Generates the Sprite: Clicking “make sprite” combines the images into a sprite using the coolRunnings service from Jared Hirsch. No zipping, no uploading form – it just happens. Figure 2 shows the newly created sprite: spriteme1.png.

Figure 2. SpriteMe makes the sprite, recomputes background-positions, and injects the sprite into the page.

Recomputes CSS background-position: Now comes the math. In Figure 1, the background-position for the #Buy element was “0% 100%”. Most sprite generation tools provide background-positions assuming every element starts off being “0% 0%”. Looking at the Alexa U.S. top 10, only 12% (56/459) of the elements that use a background image have a “0% 0%” background-position. More automated help is needed.

SpriteMe, because it runs in the web page, is able to convert the old background-position (“0% 100%”) to the exact offsets needed to use the sprite: “-10px -27px”. Converting “0%” is easy – the sprite has 10px of padding on the left, so must be offset by -10px. The “100%” conversion is harder. The original background image was 45px high. “100%” means the bottom of the image was aligned with the bottom of the DOM element. The DOM element is 28px high (as indicated by “40×28” shown in Figure 2), so we want the 17th pixel of the image to align with the top of the DOM element. This requires an offset of -17px, plus the 10px of padding results in an offset of -27px. Easy, right? Thank goodness for SpriteMe.

Injects the Sprite into the Current Page: As developers, we know it’s critical to optimize the edit-save-test cycle in whatever we’re doing. In the past, the edit-save-test cycle for spriting was painfully long: regenerating the sprite image, saving it back to the server’s “/images/” directory, editing and saving the CSS, clearing the cache, and reloading the page. SpriteMe bypasses all of that by modifying the live page as you watch. In Figure 2, the #Buy element’s CSS has been modified by SpriteMe to use the new sprite, spriteme1.png, along with the new background-position, “-10px -27px”. It might be hard to get excited – success means the page’s appearance doesn’t change at all! But injecting the sprite into the live page means developers can create sprites and verify they work in minutes instead of hours.

The Savings

In all the excitement about features, I almost forgot about the most important part: how much does SpriteMe help reduce HTTP requests? The savings for the Alexa U.S. Top 100 shows SpriteMe replaces 9 background images with 1 sprite on average, for an overall reduction of 8 HTTP requests per page. These stats are tracked using SpriteMe’s “share your results” feature – after running SpriteMe, you can choose to send the savings to SpriteMe’s Savings page. This savings of 8 HTTP requests results in an average increase of 6K in image size. This is a worthwhile trade-off, but ideas for decreasing the sprite file size are in the works.

Savings for the Alexa U.S. top 100

SpriteMe is an open source project, with all the usual links: Code, Group, Bugs, and Contact. In the second blog post, SpriteMe (part 2), I talk about my experience at WordCamp that motivated SpriteMe and the logic behind how SpriteMe makes its recommendations for creating sprites. The third post, SpriteMe (part 3), discusses issues around sprite file size (and memory size) and wraps up with thoughts on major next steps.

21 Responses to SpriteMe makes spriting easy

  1. great tool, thanks!

  2. Doesn’t seem to work with SVG-edit (which seems like a prime candidate), can anyone tell me why?

  3. What increase in file size do you consider a worthwhile tradeoff? I am finding that the image size is increasing by up to 25k for our websites with just 12 HTTP requests saved. Is there a request vs byte threshold for speed improvements?

  4. Hey Steve, you’ve done an awesome job with this tool. Saved me a ton of time, thanks!

  5. Awesome!

    I will definitely add support for this to my daemon — if feasible. A quick glance through the repository seems to indicate that this is in fact not feasible, because all logic is in JS code that must run in the browser. Why isn’t there a command-line version of SpriteMe, which you feed one (or more) CSS files and the referenced images? Or am I looking over something?

    A command-line version is far more interesting if you want to automate the generating of sprites completely.

  6. Ugh, forgot to mention what I meant by “daemon”. By that I of course meant the File Conveyor daemon that I wrote as part of my bachelor thesis.

  7. @ Brendan

    The trade-off is effectively between the time spent waiting for the download to start (affected by latency) and for the download to complete (dependent on the speed of the connection). Assuming the connection speed your end isn’t the bottleneck and that browsers are maintaining 2 concurrent connections for images, gaining 25k in weight to lose 12 requests (i.e. 6 latency hits) gives:
    Speed Latency Overall Time Diff
    2MB/s 0ms + 125ms – 0ms = +125ms
    2MB/s 25ms + 125ms – 150ms = – 25ms
    2MB/s 100ms + 125ms – 600ms = -475ms
    4MB/s 0ms + 63ms – 0ms = + 63ms
    4MB/s 25ms + 63ms – 150ms = – 87ms
    4MB/s 100ms + 63ms – 600ms = -537ms
    56KB/s 0ms +5000ms – 0ms = +5000ms
    56KB/s 25ms +5000ms – 150ms = +4850ms
    56KB/s 100ms +5000ms – 600ms = +4400ms

    In summary, if your users are on dial-up or based in your server room, it’s not worth it, otherwise the trade-off is heavily in favour of spriting. There are other factors to consider, such as maintainability and browser memory use once the sprite is decompressed but we are just talking end-user performance here.

    Please excuse any glaring errors in the maths – it’s late and my brain’s half asleep.

  8. Sorry the formatting got squeezed.
    The first value is the connection speed, then the latency.
    The next two are the cost of the extra page weight and the saving in latency, respectively.
    The final value is the resulting theoretical difference in wait time for the end user.

  9. Great tool, We used it on sonico.com that has more than 3M pages views each day.

  10. @Brendan: That’s borderline – 12 fewer requests for 25K more size. My advice:
    1) Try dragging&dropping some of the images out of there. You’ll probably find that if you remove 1 or 2 images with a lot of colors, you’ll get 10 fewer requests with only 5K more size.
    2) Take the resultant sprite image and run it through some other optimizer (smush.it or Page Speed). Right now, for load reasons, we’re not running the most aggressive image optimization.

  11. @Jeff: Can you explain more about what doesn’t work (error messages? doesn’t load?) and give a sample URL?

  12. @Martin: Did you already have http://st.ak.sonicocnt.com/133/img/sprite.png, or is that what SpriteMe created?

  13. The bookmarklet leaks the spriteme variable (check the code on jslint.com).

    Here is a fixed version:

    javascript:(function(d){var%20s=d.createElement(‘script’);s.type=’text/javascript’;s.src=’http://spriteme.org/spriteme.js’;d.getElementsByTagName(‘head’)[0].appendChild(s);})(document);

  14. @Olivier: It’s intentional to make “spritemejs” a global variable. If you declare “s” as a variable local to the function, it might get garbage collected when you exit the function. If this happens before the script is done loading, the bookmarklet won’t load.

  15. @Wim: SpriteMe uses the DOM to compute the background-positions. You could run on the commandline, but it requires a headless browsers. I’ve opened up bug #60 for this.

  16. Wim@: if you are looking for a daemon you can try Auto Sprites: web interface on http://sprites.in, code is located here
    http://code.google.com/p/web-optimizator/source/browse/trunk/libs/php/css.sprites.php

    This library solves CSS Sprites in another way – parses CSS file on server side and tries to restore full picture. It’s completely not the same as SpriteMe, gives different results and has different bugs. But can be used as an automation solution :)

  17. I can see in the results page there is a change for the bad in the size of the files? The average change in size is 8k? Why is that? http://spriteme.org/results.php

  18. With TCP slow start and this now becoming a larger single file, on top of unneeded DNS lookup times, as well as initial TCP connection isn’t Nick’s math more than a little off? Don’t forget that many unsprited pages are pulling images from two to three domains.

  19. I like the tool but I’d prefer to be able to pick which images get sprited together – or at least deselect ones that I don’t want sprited. The use case is that I might want all the social networking icons sprited together because they’ll rarely change, but SpriteMe might have picked up another image that I change frequently so I would rather keep it separate than have it sprited together with the others…

  20. @Marcus: Click “new sprite” to create a new sprite. Drag&drop images into the box to include them, or out of the box to deselect them.

  21. At last a great tool to make RSS sprites easy and intuitive. Thanks so much for sharing!