Diagonal CSS Sprites

So you’ve got your sprite created, and it’s working great. 30+ icons in one image… major HTTP connections saved. You have made your little corner of the interwebs a little happier and faster. Steve Souders would be proud.

Until you find that your sprite is used on an element that is tall enough to expose the next image component down in the sprite.  The 50px of spacing between components you thought would be enough… isn’t. 50px isn’t enough so 100px should be right? Better make it 200px to be ultra safe. So you increase the space between all of your components from 50px to 200px which increases the height by 400% and probably increases the filesize a bit too.

The 200px spacing surely is tall enough… right?  RIGHT??!? Surely no content maintainer would ever put more then 200px of content in there after you hand it off. *gulp*

I figured there had to be a better way to make sure this didn’t happen. Horizontally spacing the sprite would solve the too tall issue, but then it has to be spaced out pretty decently as well. Probably even more than the vertical spacing.  What I wanted was the best of both worlds. Horizontal AND vertical spacing.

What do you get when you both horizontally and vertically space something? A diagonal.  I searched around but couldn’t find any mention of a diagonal sprite. I didn’t know if that meant this was a new idea, or a bad one that no one was willing to publish. I’m hoping it’s the former.

Why a Diagonal Sprite?

With the sprite built on a diagonal there are no components below or to the right of the component you are showing. This allows for the element using the sprite to be as wide or as tall as it needs to be with no worry of exposing the next component.

Unfortunately there is a filesize tradeoff. You add a LOT of unused whitespace. For one 30 or so component sprite that I changed from vertically stacked to diagonally stacked, there was only about a 10% increase in size.  For another that had around 60 images, it was a 45% increase. The 10% increase was pretty easy to swallow for the benefit gained. The 45% is a bit harder. So your mileage may vary.

Since you don’t have to put as much spacing between your sprite components (I do 10px vertically and horizontally to allow a little per component manipulation) the sprite dimensions are significantly smaller.  My 60 sprite image with 50px of space between components was 2142px tall. Spacing it out to 200px between components took it to over 8000px. The diagonal sprite using the same 60 components, with 10px padding ends up being around 1500px tall and wide. As far as I can tell this only really matters to the Opera browser. I can’t find information on any other browser having issues with ginormous dimensions, but would love to hear feedback if you know of other issues.

So that you can see this in practice, I created a simple 10 component sprite from a few icons from the famfamfam silk icon set (seen above). You can view an example page that shows the 50px, 200px and diagonally spaced sprites. The filesizes and dimensions ended up being: 50px – 16×610 5,723 bytes, 200px – 16×1960 5,967 bytes, diagonal – 250×250 6,554 bytes.

Resize the page to be fairly narrow to get content to wrap and even the 200px spaced sprite exposes an additional component.

Making the Diagonal Sprite

When making my normal vertically stacked sprites I use the fantastic online tool CSS Sprite Generator.  I noticed that the back end for that was open source and so grabbed a copy.

After significant pain in getting the required components installed on my local machine (post on that coming later), I tweaked their code to output the sprite in a diagonal. At the moment the code is kind of quick and dirty since I wasn’t sure if it was going to work. If there is interest in me making it available, please let me know in the comments below and I’ll try to get it cleaned up and released.

In the end I think this is a pretty decent solution to the problem I ran into. It probably doesn’t work for everyone and the filesize increase is something to consider, but it is an interesting option to have in the back pocket. I would love to hear your thoughts on this solution.

88 thoughts on “Diagonal CSS Sprites”

  1. I’ve taken to using a checkerboard-like approach where there is always space directly to the right and to the bottom of each sprite (in addition to about 10px of padding so that there is also some space between the sprites on the diagonal). Sadly, the potential is still there for the container to reveal unwanted sprites.

    WIth the method you’ve outlined, you could potentially have repeating sets. In which you could have 10 sprites in the diagonal, then start over for the next set (visually, it’d be like taking the example image and tiling it horizontally x number of times).

    I agree with the others, would love to have a generator made available.

  2. @everyone – I’ll work on getting something published for the generator. My server supporting the required components may be a hiccup but we shall see.

    @Jesse Wilson – I actually went down that path and got the generator to do the wrapping for me. Sadly it doesn’t work as well as you might think.

    To make things easier I think it’s best to illustrate. Check this image out.

    That is a test sprite I made that wrapped at 500px. You can see that you quickly run into height and width restrictions before you start running into other sprite components.

    As a filesize comparison, my ~80 component sprite is:
    – One long diagonal = 1552×1552 37,798 bytes
    – Wrapped at 1000 = 1000×1552 34,200 bytes (10% savings of one long diagonal)
    – Wrapped at 500 = 500×1552 30,914 (18% savings of one long diagonal)

    So you get some file savings, but are back to square one of colliding with other components. You could add extra padding to make the colliding less likely, but now your filesize is back up and you could still theoretically bump into things again.

    But glad to hear I wasn’t the only one with the idea. I had high hopes for it, but as you can see it didn’t pan out.

  3. Interesting, but even more so would be to do some comparisons in terms of impact on the RAM required to render those huge images (big chunks of white pixels compress very well on file, not so much when the browser has to render them on screen). Have you looked at that aspect in your comparisons?

    1. Yeah, compression is one thing, the actual memory footprint is another. 1500 x 1500 pixels is too large for a sprite, especially if you care about underpowered devices.

      A 100×100 image is 10,000 pixels. 1000×1000 is 1,000,000 pixels. Compressed the file size is one thing. The memory footprint (uncompressed and displayed in the browser) is another thing entirely. Those pixels stop being skipped over with compression techniques when the browser actually gets down to rendering it.

      I did a quick and dirty comparison of the memory footprint of large sprites a few months ago.

      1. Very interesting read.

        I’m in no way trying to downplay your findings, but in looking at your table of results it seems that the memory used semi tracks with the addition of filesize. Is the additional memory used related to the dimensions of the image or to the filesize?

        Meaning if you had one file that was 1000×1000 and 10k, and you had another file that was more complicated and was 100×100 and 10k, would the memory usage be the same for both, or higher for the file with the larger dimensions. If higher, is it significantly higher, or just a little bit?

        Also, what is the real impact of this for the user? In this day of multiple gigabytes of memory available, does a few extra K of memory have much effect? Is memory used for images reallocated well (as opposed to JS that has memory leaks and stuff)?

        Again, not trying to diminish your findings at all. The thought of memory use hadn’t entered my mind at all and these are just things I wonder about as a result.

        I am a major advocate for performance at my work. So if this truly is a performance hit, I would be the first to stop using it.

        Although I would guess that for smaller sprites, say 10 components or so, that are running into the situation this fixes (accidentally showing additional components) the memory cost is negligible and so this is still a viable solution.

  4. Great article! Would love to see a generator for this. I actually started doing sprites like this at the company I work for a few of years ago, but it became really large and unwieldy (particularly with multiple people working on it at the same time) and we eventually moved to a regular sprite generator. Would love to use a diagonal sprite generator. The only issue we ran into (at the time) was that Safari needed extra white space on the right side of the image or else the sprite would tend to repeat for reasons I cant recall anymore (and Safari has had a few version bumps so its probably not an issue anymore). Looking forward to checking out what you publish for it!

  5. @everyone – I made a quick attempt at getting the sprite generator working on my webhost (dreamhost). The script requires imagemagick which they don’t have installed as a php extension by default and I don’t have the chops to try to get a custom install working. The script also needs optipng (not required, but sure saves on filesize) which also isn’t installed.

    So it may take a while to get something functional up and running. I will at least try to clean up my code and post it for others to try and install in their own environments.

  6. Re: memory usage, here’s an interesting link:

    http://blog.vlad1.com/2009/06/22/to-sprite-or-not-to-sprite/

    The biggest problem with CSS sprites is memory usage. Unless the sprite image is carefully constructed, you end up with incredible amounts of wasted space. My favourite example is from WHIT TV’s web site, where this image is used as a sprite. Note that this is a 1299×15,000 PNG. It compresses quite well — the actual download size is around 26K — but browsers don’t render compressed image data. When this image is downloaded and decompressed, it will use almost 75MB in memory

  7. I just wanted to point out, in the case that it was not immediately obvious, the empty space to the top and left of the diagonal sprites could be used for additional sprites which are to be used in fixed-size elements (where it can be assured that no ‘bleed’ space will be required), alleviating some of the file size increase due to blank pixels.

    1. Great point. Not easy to accomplish in a sprite generator (if I ever get it working) but definitely good to keep in mind when manually maintaining a sprite.

  8. I don’t understand why you can’t just apply “background-repeat: none” to the element if you don’t want it to repeat the sprite image. I’m sure I’m just missing something.

  9. Memory consumption definetely is an issue, as every sprite will stay in memory *uncompressed*, so even whitespace will consume its 32bit per pixel. This is why I’ve found my love for Data-URI-Sprites. I think they are a valuable alternative to the “old-school” CSS-sprite. I lately built a little webapp for that, spritebaker.com and still need help in debugging, so every feedback is welcome :).

  10. Maybe it’s better for the user point of view (memory) to make two or three sprites, one for horizontal elements, one for vertical and maybe one more with no space needed around. So the image dimensions keep small, two or three http connections should not be a problem for “normal” websites.

  11. Diagonal sprites are a great solution for left aligned background images. But for right aligned images on elements with flexible widths, where a % offset is required, I believe this solution falls short. Does anyone know a workaround for right aligned images using diagonal sprites?

      1. That would work if for fixed width elements. However, with a fluid width, since it would require a % background offset (eg. 0 100%), this solution would not work.

  12. This solution is problematic for maintaining.. unless your sprite generator also automagically generates the css too (which is feasible).

    What do you do if you come back in a month and want to add one more image? You have to bump all your images by one unit and rewrite all their css values.

  13. I normally use a span tag with the normal sprite background and overflow hidden, but this way you can avoid the use of extra markup.

    I dont know wich of these two options can save more weight

  14. Aaron,
    I have been working on a sprite generator that uses FileReader and Canvas to run inside of your browser without any uploads. It is still in super early stages and probably has some bugs, but you motivated me to add a diagonal mode in it. See instantsprite.com and let me know what you think.

    One thing I am still trying to work out is how the HTML/CSS should be generated. Every case is different. Sometimes I want to use it on the side of a paragraph (like you are here), and sometimes I want to have it replace text entirely. Additionally, if you are trying to sprite existing images/css/html, the background positions may be the only thing you are interested in…

    The maintenance is something that has been mentioned that is really important to me. I usually only sprite small images that are being used for one purpose per sprite, so copy/paste generated CSS works pretty well for me (my images don’t change THAT often), but if anyone has ideas how a tool could make that easier, I’d consider adding it in to Instant Sprite.

    Brian

  15. Howdy there, are you having issues using the internet hosting? I needed to refresh the web page about substantial number of times to be able to get the web page to run!

  16. And if you now run those sprite PNGs throu pngquant (128 colors) or plainly save em as PNG-8 images, you will notice that both are at nearly the same size. The diagonal sprite PNG is about 9% bigger than the regular one, but at 2k thats probably negligible.

    cu, w0lf.

Leave a Reply

Your email address will not be published. Required fields are marked *