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:
Considering CSS sprites were something that dhh mentioned as putting into Rails 3.1, maybe you should see if you can just get that moved into Rails core, before the sprites work starts.
Great suggestion. I presume they'll want to roll their own solution (hopefully taking advantage of existing gems like ruby-yui-compressor and closure-compiler). But I'll open a ticket for it.
> I presume they'll want to roll their own solution
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.
The demonstration of speed difference is bogus. Really we should be comparing Jammit vs Sprite load time. Of course 100 files will take longer to load than 1 file!!! But how about 1 extra sprite vs Jammit? Here's a demo:
mmh... there are two kinds of overhead associated with using data URLs.
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.
Maybe. The JPEG decompression has been mercilessly optimized for decades.
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.
The huffman coding in gzip reduces this down to almost nothing, as the base64-encoded data only has 6 bits of entropy per byte, which negates the 1/3 increase in 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.
For example, you can't re-use the same image in other CSS files without duplicating the image's download cost as it's the surrounding CSS file that's cached, not the image itself. 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. Fine in some cases, not so much in others.
> For example, you can't re-use the same image in other CSS files without duplicating the image's download cost as it's the surrounding CSS file that's cached, not the image itself.
On the other hand, you could create css classes like
well, yes and no; having them put away into a separate css file would be an extra http request, but it's not fair to say "therefore you might as well use sprites", because the chances are you're going to have a global stylesheet that you can tack the images onto the end of; no extra HTTP requests.
Sorry, poor choice of words. It brings the size down to almost the original and is probably irrelevant. Off course this depends on the site you have. It is possible that in some cases you will get substantially bigger files.
In my experience it's typically less than 100 bytes. You'll struggle to squeeze the HTTP overhead below that.
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.
Firefox keeps a copy of every image on screen decompressed in memory anyway, that's one of the downsides to using large sprites, they can take up a large amount of RAM. I doubt non-sprited base64 images compare in size issues.
Ideally, in my opinion, when the browser makes a request for example.com/something the server should send a continuous stream of data. It could work something like this:
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...
Yes. HTTP/1.1 arranged for this with pipelining 11 years ago.
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.
Pipelining is not enough even if the internet infrastructure was perfect. Why?
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.
True, if latency dominates that much in your situation, which can happen with no congested pipes in the path. An HTML5 "manifest" with pipelining would take care of all but the initial and 2nd latency.
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.
You basically just described HTTP pipelining, which has been part of HTTP for over a decade.
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.
One downside that hasn't been mentioned so far, as far as I can tell: the conflict between caching and downloading unneeded image data.
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.
You would have exactly the same issue when applying CSS sprites... the only difference here is including the image data in the CSS file rather than a separate sprite file - removing the need for another HTTP request.
I believe stylesheets must be downloaded first before any further rendering can be done. Therefore you block rendering until all images have finished downloading, while a traditional external image sprite can be downloaded asynchronously.
I imagine you could put your base64 encoded images in a separate CSS file to solve this, but then you are back up to one extra HTTP request like CSS sprites.
I was going to make this point on the OP but my comment wasn't posted (yet?)
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.
I'll add... it really makes sense for mobile/webkit where you can depend a lot more on CSS3 and most images are small icons or tiles. I've seen Apple use it a lot in their mobile web apps.
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.
I seriously doubt it. You only remove 1 extra request with this technique. The main advantage is that you don't have to maintain a sprite image (but using this without some tool would also be cumbersome).
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.
in defense of the technique: Parsing an existing CSS file, extracting the images, base64-encoding and embedding them is something that's very easily done programmatically and a script that does this on deployment is very easily created.
Assembling a huge sprite image and emitting offsets is harder. Still very doable though, of course.
One additional downside is that sprite atlases are annoying when you need to tile the image. Vertically repeating images can't be in the same atlas as horizontally repeating ones. X+Y repeats each need to be in a separate image altogether.
I'm also wondering if, to correctly emit the offsets in CSS attributes in all cases, you'll require some manually inserted placeholders.
Are there specific game engine tools that could be re-used for web work? It's a rather tedious process at the moment, pulling out sprites and arranging them on a sheet. Keeping them updated and so forth.
What CWIZO said. But I wish to emphasize that this is a great example of just how nice it would be in a world without ie6/7. I got excited reading about this technique, but the maintenance work associated with including ie hacks makes the technique, for the time being, basically worthless.
Mobile browsers tend to do less caching, won't cache larger files, and don't caches files for as long. Doing an end-run around their image caching seems prone to cause problems.
Has anybody tried this technique on a mobile browser, say Mobile Safari on iPhone, to see how it stacks up?
Is anyone else but me worried about the legibility of the CSS with Data URIs? I mean, I can see how it can be useful, but I think the code is more readable with "spritesheet.png" than with:
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP
C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA
AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J
REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq
ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0
vr4MkhoXe0rZigAAAABJRU5ErkJggg==
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 certainly wouldn't write it inline in the CSS. I'd use a preprocessor to keep the style itself readable and have the base64 inserted for the deployment version.
I personally like Sass, but for this purpose CPP or a knocked-together Ruby script would be just as good.
It would be useful to see a matrix of browser/version support for data URI's embedded in external stylesheets along with anticipated support in future versions.
With data URIs, using the same image in two different CSS rules will result in two instances of the base 64 representation of the image in your CSS file. There are workarounds...none are pretty though. MHTML does not have this problem. In fact, in some ways I think MHTML is a more elegeant format for asset delivery.
As I remember when a sprite was a hardware assisted moving image on a screen, I really dislike how this term has been adopted. The fact that it uses a section of a large image does not make it a 'sprite'.
A sprite is just a 2D image. Animation in old 2D games would usually store several frames of animation in a single image, similar to the current use in CSS.
So, where's the relation to animation here? I know what a sprite is, thanks. It sounds like you might want to go read the Wikipedia page, on the other hand.
In computer graphics, a sprite (also known by other names; see Synonyms below) is a two-dimensional image or animation that is integrated into a larger scene.
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.
I still think it is all the 20 year olds who have never programmed or used a system with hardware sprites that have no idea what they're saying, but I do appreciate the generous sentiment. Sure, if one is perceived to be wrong AND jerky and insistent about it, as I was, people tend to downvote your comment.
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...