Data URIs make CSS sprites obsolete
nczonline.net
nczonline.net
http://documentcloud.github.com/jammit/
The big advantage here over spriting is not having to deal with the complexities of trying to jam repeated background patterns into sprites, and having to compute pixel offsets for your CSS rules. You can just write your CSS as you normally would, and have the plugin generate appropriate versions of your stylesheets before deploying.
If you'd like to see a demonstration of the speed difference it can make, here's a page that loads 100 small individual images:
http://jashkenas.s3.amazonaws.com/misc/jammit_example/normal...
And here's the same page, using a single CSS file with data-URIs for all the images.
http://jashkenas.s3.amazonaws.com/misc/jammit_example/jammit...
I have yet to contribute to rails-core, but generally, they don't accept feature requests, they only accept patches. Even if you end up replacing the 'back end' of it with something else, maybe your code would give the feature a head start.
One is that encoding arbitrary binary data using base64 will increase the size of the data by around one third, so you will transmit more data.
Of course, if you do the caching headers right, this applies only to the first connection.
The other overhead you have to pay for every time: The data has to be base64 decoded before it can be used. Depending on the amount and size of images and depending on the type of implementation in the browser, this can require a significant amount of resources.
edit: In addition I wonder whether browsers cache the base64 decoded output between page loads. If you are really unlucky, the decoding might be done on every page load. Also, I'm not sure how browsers would handle identical data-content in multiple CSS rules: Do they keep multiple copies of the same image in memory? Or do they detect identical data and keep only one image around?
I would do some tests before blindly applying this.
Anecdotal evidence: I just base64+gzipped a JPEG with 13668 bytes. Result: 13713 bytes, delta: 45 (0.3%).
Command used:
base64 -w 0 infile | gzip -9 -n > outfile
For HTTP you can even use raw deflate, which does away with some of the gzip file format overhead. IIRC this saves you another 18 bytes for the header (10 bytes) and footer (8 bytes).Tangent: the reason I'm such a gzip nerd is that I used it to implement compression for level assets in Battalion Wars 2 for the Wii. I didn't want to make up yet another format, and gzip was ideal save for the fact that the size of the raw data is stored at the end of the file. We needed the info to allocate the necessary memory, but seeking to the end would have caused some latency from the DVD drive mechanism. The solution was to add a proprietary header which is ignored by the gzip code and the rest of the toolchain.
Individual image sizes: 1811, 1809, 1774, 1817 bytes
Combined in a sprite: 5970 bytes
Individual images after base64: 2448, 2444, 2400, 2456 bytes
Individual images base64+gzip: 1890, 1887, 1858, 1901 bytes
Concatenate base64 files to simulate embedding in a CSS file: 9748 bytes
Concatenated file + gzip: 7073 bytes
In practice the difference is less than the HTTP protocol overhead for an extra request.
There are other reasons why you might not want to use data URLs, but this isn't a valid one.
Which?
On the other hand, you could create css classes like
.someImage { background: url(...) no-repeat; }
and stick them in a sitewide stylesheet.On your further answers, data URLs are far better than sprites in maintainability, not(no rewriting of the map etc)?
At the same time, you download all the data URL images with the CSS, regardless of whether the user ever gets to see them on the current page.
Thats also true for sprites, i think.
Compared to decompressing the PNG or JPEG data, decoding base64 is essentially free. It will therefore also not matter much at what stage it's cached.
The oddball base64 URI decode might have been done by the team's crappy coder that no one trusts, but you can write decent test cases for base64 decoding, so you can burn his hours for a few weeks without him doing any lasting harm to the code base.
See the discussion on http://blog.mozilla.com/webdev/2009/03/27/css-spriting-tips/
1) The browser sends the request along with a list of files it already has cached (maybe a special type of cache that never expires so the browser would know that it wouldn't need that file ever again).
2) Then web server sends the actual HTML that needs to be rendered, followed by whatever files the browser may need (css, js, images, etc). The browser would know where the HTML block starts and where other pieces begin, so from its point of view it's the same as if it requested those pieces. The difference is that the server anticipates these requests and sends them along all in one batch. The very first piece would be a list of files the server is sending, so the browser would know what to expect. At the end of the stream, if there are any more files that are needed that haven't been included in the main stream, the browser would just request them as usual.
Doing something like this would effectively render most page accesses to a single request, going from 10 or more requests to a single request would have quite a bit of an impact. For this to work though, both the browser and server would need to support such a feature...
The main problem is that somebody might have installed some broken proxy in the middle that doesn't understand HTTP pipelining. That is why browsers usually disable it by default. The second problem is that sometimes it is faster to open multiple parallel connections.
But there were servers that thought they supported pipelining and had corruption issues, so the clients got scared and wouldn't use it. (Besides, the modems on the edge of the web were the problem, not the latency.) Then the proxy people said "Why bother? No one uses it.", and the clients continued to not implement it, or did but left it off by default with a switch to turn it on in a disused lavatory, behind the "Beware of the Leopard" sign. Meanwhile, the server people having run out of useful and useless features to add to their vast code bases actually got around to making pipelining work correctly.
Welcome to 2010.
• Most popular web servers support pipelining, probably correctly.
• <1% of browsers will try it.
• Proxies (firewall and caching) largely break it.
• If you are using scripts that can change the state of the web server, then your head might explode when you consider what happens with pipelined requests.
1. Slow requests stall the entire pipeline. 2. The initial request stalls the pipeline. 3. The pipeline is being pulled instead of pushed so your performance is still roundtrip-lag-bound if you have cascading references. For example an HTML file references a .css file which references PNG costs 3 round trips at least.
SPDY is definitely implemented in Chrome/Chromium, though I don't know if it's on by default. I believe at least some google services (google.com in particular) support it publicly as well.
Some zooming browsers also mess up sprites on the edges at certain zoom levels.
If you use image 1 on pages A & B and not page C, but image 2 on pages A & C but not B, you face a dilemma.
- If you stuff it all in one big CSS file, visitors to B (C) will download image 2 (1), even though it's never shown.
- If you split the CSS in 2, with A including both and B & C including only one each, you'll have an extra request in A, and require duplication or a third file if B & C share style rules
- If each page serves its own CSS file, you have only 1 request but it (and the image data) won't be cached across pages.
This restricts this technique to images that are used all over the place or images that are tiny.
There are ways around it (define all your background images at the end of the CSS file for example), but I think it's a bit early to be declaring data-urls the holy grail of page performance.
And the ie6/7 stuff isn't much to worry about, either - you can just use conditional comments to include a traditional CSS Sprite-based file if ie6/7 is detected.
Yes, it means more overhead, maintaining two versions of your CSS file, but the improvements in speed might be worth it.
IMHO one saved request isn't work doing the same thing twice. And MHTML (http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-a...) doesen't come as a saviour either, as you have to duplicate all images in the CSS, which increases the size of the CSS significantly (if it doesn't you are probably better off using "old fashioned" techniques.
Assembling a huge sprite image and emitting offsets is harder. Still very doable though, of course.
Game engines has done this for almost 20 years.
I'm also wondering if, to correctly emit the offsets in CSS attributes in all cases, you'll require some manually inserted placeholders.
Such a shame.
Has anybody tried this technique on a mobile browser, say Mobile Safari on iPhone, to see how it stacks up?
Now if only there was a way to define a data URI in a similar manner to @font-face. Maybe @data-object or something like that.
I personally like Sass, but for this purpose CPP or a knocked-together Ruby script would be just as good.
http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
This dates back to the Commodore 64, that said do we really need to down vote this guy so much. He was misinformed. This doesn't mean we have to be vindictive.
Anyhow, it's just terminology. I still hate it.