Aptimize: realtime spriting and more
I love evangelizing high performance best practices and engaging with developers who are focused on delivering a fast user experience. Helping to optimize the Web appeals to the (speed)geek in me, and my humanitarian side feels good about creating a better user experience.
There are many companies and developers out there that go the extra mile to deliver a fast web page. My hat’s off to you all. It can be hard to maintain a high performance development process day-in-and-day-out. Heck, it can be hard to even understand all the performance optimizations that are possible, and which ones make sense for you. I savor the challenge this presents, for the same reason that I resonate with what Jimmy Dugan (Tom Hanks) says to Dottie Hinson (Geena Davis) in A League of Their Own when she complains about how hard playing baseball is:
It’s supposed to be hard. If it wasn’t hard, everyone would do it. The hard… is what makes it great.
As much as I love the skills it takes to build fast web sites, I also want a majority of sites on the Web to get a YSlow A. Right now, the bar is just too high. For fast web sites to become pervasive, web development needs to be fast by default. Performance best practices need to be baked into the most popular development frameworks and happen automatically. We’re seeing this with browsers (parallel script loading, faster JavaScript engines, etc.) and in some frameworks (such as SproutCore and WordPress). I predict we’ll see more frameworks and tools that deliver this fast by default promise. And that’s where Aptimize comes in.
I’ve been tracking Aptimize for about a year since they contacted me about their Website Accelerator. I was psyched to have them present at and sponsor Velocity. Website Accelerator changes web pages in real time and injects many of the performance best practices from my books, plus some others that aren’t in my books. It’s a server-side module that runs on Microsoft Sharepoint, ASP.NET, and Linux/Apache. One of their more high profile users wrote up the results last week in this blog post: How we did it: Speeding up SharePoint.Microsoft.com.
Here are some of the before-vs-after stats:
- # of HTTP requests empty cache went from 96 to 35
- # of HTTP requests primed cache went from 50 to 9
- empty cache page load times were reduced 46-64%
- primed cache page load times were reduced 15-53%
These results were achieved by automatically applying the following changes to the live pages as they left the server:
- concatenate scripts together (goes from 18 to 11 requests)
- concatenate stylesheets together (goes from 17 to 5 requests)
- create CSS sprites and inline images using data: URIs (goes from 38 to 13 requests)
- add a far future Expires header
- remove duplicate scripts and stylesheets
- minify (remove whitespace, etc.) in scripts and stylesheets
You can see the results by visiting sharepoint.microsoft.com. (Compare the optimized page to the before page by adding “?wax=off” to the URL. Get it? “wax=on”, “wax=off”?? The Aptimize team is from New Zealand, so they’re always up for a good laugh. Note that once you use the querystring trick you might have to clear your cookies to move between versions.) You know you’re looking at the optimized page if you see URLs that start with “http://sharepoint.microsoft.com/wax.axd/…”.
With my recent work on SpriteMe, I was most interested in how they did CSS sprites and used data: URIs. Here’s one of the sprites Aptimize Website Accelerator created automatically. They don’t create it on-the-fly for every page. They create the sprite image and cache it, then reuse it on subsequent pages.
I was especially impressed with the work they did using data: URIs. Data: URIs are a technique for avoiding the extra HTTP request for files (typically images) by embedding the encoded response data directly in the HTML document. The main problem with data: URIs is that they’re not supported in IE7 and earlier. Recent blogs from web dev performance rock stars Stoyan Stefanov and Hedger Wang discuss a workaround for early versions of IE using MHTML. Aptimize has already incorporated these techniques into their product.
To see this in action, go to sharepoint.microsoft.com in a browser other than IE. I’m using Firefox with Firebug. Inspect the printer icon next to “print friendly” at the bottom of the page and you’ll notice the background-image style looks like this:
url(data:image/gif;base64,R0lGODlhGAAaALMJA...
That’s the base64-encoded image. It’s declared in the MSCOMSP-print
rule inside this Aptimize-generated stylesheet.
Inspect the same “print friendly” icon in IE7 and you’ll see this background-image:
url("mhtml:http://sharepoint.microsoft.com/wax.axd/cache/2fpw31-aG1G4JD2$a$MeCg.mhtx!I_aWWORG5UHNr6iB8dIowgoA")
This is worth understanding: “mhtml” tells IE this is MIME HTML. The URL, http://sharepoint.microsoft.com/wax.axd/cache/2fpw31-aG1G4JD2$a$MeCg.mhtx, points to multipart response. The location, I_aWWORG5UHNr6iB8dIowgoA
, identifies which part of the multipart response to use. There we find the same base64-encoding of the printer icon.
Most sites avoid data: URIs because of lack of cross-browser support. The MHTML workaround has been known for awhile, but I don’t know of many web sites that understand this technique, let alone use it. And here we have a tool that adds this technique to your web site automatically. Well done, Aptimize.
Schepp | 06-Oct-09 at 2:52 am | Permalink |
Some time ago I also stumbled across Stoyans Blogpost(s) and incorporated his and Hedger’s techniques.
I naturally read your book and on that very day you made a performance-junkie out of me.
By coincidence, I just finished a little PHP-library that does all the performance-boosting stuff on CSS and JS automatically for you. Including dataURI and MHTML.
But it also re-splits the CSS for example to have it load in parallel over two HTTP-1.1-connections, like pointed out recently by David Maciejewsky and Dirke Jesse at the BOA2009. Here you find their slides, helas in german: http://www.slideshare.net/dmacx/performance-optimierung-barrierefreiheit-beginnt-mit-ladezeiten
my library is still rough but if someone likes to give it a test-drive:
http://github.com/Schepp/CSS-JS-Booster
Regards from Germany
Christian “Schepp” Schaefer
Miller Medeiros | 06-Oct-09 at 11:17 am | Permalink |
These guys created a tool to automatically generate DataURIs for every CSS image and it works cross-browser (even if javascript is disabled).
http://duris.ru/
I’ve had the same idea sometime ago but after searching I discovered that someone already did it..
sunnybear | 06-Oct-09 at 1:49 pm | Permalink |
@Miller: thank you for link, we have worked hard to make this technique real. But there is a bug under IE7@Vista — it can be resolved only with JS.
I think we will integrate our experience into Web Optimizer (www.web-optimizer.us) soon (and there definitely must be served different CSS files — for IEs and the others — as Aptimize already does).
About Aptimize approach: it’s not generally the best one. It uses 2 different CSS files – one for rules and one for mhtml images (for example webo.in uses the same for both properties). If you open website in IE7 you can find the CSS
http://sharepoint.microsoft.com/wax.axd/cache/C4QdXr3Hxp-3cyCuaCfsyA.css
and here is the call to base64-image from the file
http://sharepoint.microsoft.com/wax.axd/cache/P9jWHcZuZVbTlYpb9wB7tw.mhtx
Files are different that leads to additional HTTP request. So there is a place for additional improvement.
P.S. noticed in CSS code
margin:0px
32px
0px
0px;
very funny, that Aptimize doesn’t shrink variables :)
Derek Watson | 06-Oct-09 at 3:10 pm | Permalink |
Thanks sunnybear for your opinion and for trialing our product.
The Aptimize approach of separating the CSS + image data provides the best balance of overall page load time, time to start render, and reuse of cached resource sets. Bloating CSS files with large amounts of MHTML data is not good for the start render time of the page, so there’s always a trade off to be made.
Shrinking CSS variables does not offer any significant benefit in page load time, and can in some cases affect rendering behavior, so we made a conscious choice to focus our efforts on the more important optimizations.
Derek Watson
CTO, Aptimize.
Schepp | 06-Oct-09 at 3:47 pm | Permalink |
Not to mention compatibility. As User Hedger points out in his comment as PHPied http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/#comment-70734 he had the most flawless experience by having the MHTML served as a seperate file.
aaron | 06-Oct-09 at 5:31 pm | Permalink |
If the MHTML url still requires a separate download, I don’t see any advantage to using it in IE. Why not just use a url pointing directly to the image/sprite? Is the base64 version smaller or am I missing something?
Aaron Sinclair | 06-Oct-09 at 7:42 pm | Permalink |
The MHTML contains ALL of the images for that document, not just one. So instead of pulling down each image as a separate request, all images get pulled down in one request.
In summary: WAX detects the browser type, and uses Data URIs for FF, Safari, Chrome, IE8 – and MHTML for IE6, IE7.
Its a trusted solution, giving an enterprise level result for websites, and is safe for all browsers.
Steve Souders | 06-Oct-09 at 7:58 pm | Permalink |
@aaron: The MHTML/data: URI images are not always background images – they are frequently IMG images. Although it’s possible to use sprites with IMG images, it’s not always preferable. For example, most browsers don’t print background images (unless the user changes this in their options). Converting IMG images to sprites straightaway could lead to problems. Changing them to use MHTML/data: URIs avoids the problems, while eliminating the extra HTTP requests.
Schepp | 06-Oct-09 at 11:40 pm | Permalink |
@aaron Other disadvantages of sprites over data:URIS are also:
– if your customer desides that for example an icon, which happens to sit in the very middle of the sprite, shall be replaced by a fairly larger one that doesn’t fit into the room left, you need to move everything around and recalculate background-positions for everything down the sprites-chain (except for when the sprite is created automatically)
– you can’t puzzle together a large bunch of different-sized pictures without leaving a lot of unsed white-space in-between.
– you also can’t mix file types within one sprite, so if on the one hand side you need a lot of transparencies and on the other side have a lot of photos you also need to put together two sprites.
I don’t know about file-size-comparisons between sprites and GZipped data:URIs, but as long as they aren’t huge I don’t care. Most of the “faster”-experience comes from circumventing HTTP-requests (by means of reducing them or smart caching), not shaving off one more KB from data load. Most of our typical clients today have really fast broadband connects and suffer primarily from latencies.
aaron | 07-Oct-09 at 9:36 am | Permalink |
Thanks for clarifying guys. I love that you can download all images in one request. I’ll definitely be looking into using this technique in the future.
sunnybear | 07-Oct-09 at 9:56 am | Permalink |
@Derek: I agree that the only file isn’t the best choice. For webo.in I’ve tested anther approach: load additional images (data:URI + mhtml) on onDOMloaded event. This provides minimal time that is required to get page ‘styled’ (actually w/o background images). 2 files of course are cached better.
About shrinking variables. I think providing
margin:0px 32px 0px 0px;
is a bit stupid when we are talking about eception performance. YOu can absolutely safely shrink this to
margin:0 32px 0 0
In terms of gzip there won’t be big difference, but we can also apply alphabetic order (which is already integrated into Web Optimizer), etc, etc.
At the bottom line I think that automatized solution MUST incorporate all the BEST practices. Not just a general approach to ‘combine and minify’. And we are developing Web Optimizer due to these high standards.
@Schepp: there is workaround for images of different size (it’s integrated into CSS Sprites module of Web Optimizer, and it seems going to be integrated into SpriteMe). And PNG images (with their compression) already solved this issue — losses are minimal.
Ruslan | 08-Oct-09 at 12:10 am | Permalink |
About MHTML+IE7@Vitsa – where I can find working examples with MHTML for IE7@Vitsa with change background image on hover?? sharepoint.microsoft.com contains only STATIC images, not have any MHTML image with dinamic changes!
Schepp | 08-Oct-09 at 2:27 am | Permalink |
@Ruslan It just works, like it does work with sprites.
MHTML only changes the URL/source of your image, nothing else.
You can try it out with my PHP-Library mentioned above, should your page happen to be wirtten in PHP.
Ruslan | 08-Oct-09 at 5:28 am | Permalink |
@Schepp, I have experience in building MHTML for IE7 @ Vista, if the css file is cached then hover does not work. Can you make online example with MHTML for IE7 @ Vista?
Schepp | 08-Oct-09 at 6:29 am | Permalink |
@Ruslan I put together a testcase for you that you’ll find inside the my package now (example/example2). But you will need to download and host that yourself.
Ruslan | 08-Oct-09 at 10:35 am | Permalink |
@Schepp, your examples2 not work on IE7 @ Vista(x64). I see first image blue and after hover background becomes white (not magenta), and more, when end of hover, background remains white (not blue).
MHTML + IE7 @ Vista = is big problem
Schepp | 08-Oct-09 at 9:44 pm | Permalink |
@Ruslan Not so good to hear. The problem is that I currently don’t have such an exact setup at my hands to test that out myself. Thinking about skipping MHTML-ification of all :hover-selectors now… Anyhow, thank you for your input on this specific “bug”. Wouldn’t have found out myself for some time!
sunnybear | 09-Oct-09 at 2:53 am | Permalink |
@Ruslan, @Schepp: I think that the best solution is in the right combination of mhtml, data:URI, and CSS Sprites approaches. Each of these techniques is great, so their combination will be killer-feature. Also we can just exclude images from mhtml/data:URI processing to prevent such side effect.
Ruslan | 09-Oct-09 at 4:13 am | Permalink |
@sunnybear, I don’t think that the mix of data:URI+mhtml with standart CSS Sprites is the best idea. I think after some time the CSS sprites become extinct like the dinosaurs. data:URI it’s killer of CSS sprites, not friends :).
Jamesuk | 09-Oct-09 at 4:26 pm | Permalink |
A sincere thanks for this post Steve. We have been hard testing Aptimize for the past 4 days and I would like to tell you that the results have been nothing short of amazing. 42% increase in first page load time – 51% increase in repeat page laod time. The software also appears to have fixed a significant caching issue that we have been scratching our heads about for the past three months. We have further testing to do but I just wanted to send my regards for bringing this to our attention.
Schepp | 11-Oct-09 at 10:22 am | Permalink |
@Ruslan I fixed the bug by skipping MHTML-ification of background-images on :hover-selectors. Tested on a virtual maschine with Vista and IE7.
If you happen to redownload my package on GitHub: I moved the testcase into /testcases/testcase1/index.php
Ruslan | 12-Oct-09 at 1:49 pm | Permalink |
@Schepp, Ha-ha! how you solved problem? added standard background-image?
.div1:hover{background-image:url(/booster/./../testcases/testcase1/css/../images/hoverstate.gif);color:#66F}
this solving is not interesting.
And after reload page – background is WHITE (not blue) = after page reloading MHTML not works.
Sorry, but Vista@IE7 + MHTML is incompatible.
Schepp | 13-Oct-09 at 5:03 am | Permalink |
@Ruslan I didn’t mean to make it interesting. I did mean to fix this broken look.
For the type of bugfix you are thinking about, grab your phone and call Microsoft.
Ruslan | 13-Oct-09 at 8:49 am | Permalink |
@Schepp, Forgive me, if I have somehow offended for you.
@sunnybear already spoken with representatives of Microsoft – at the out ZERO result.
Once again, you want to point out the fact that it is useless fixes Vista @ IE7 + MHTML. After rebooting MHTML images disappear.
I am very shocked that there is no normal solution.
Ruslan | 21-Oct-09 at 11:09 am | Permalink |
I conducted a study how works the Aptimize for IE7@Vista.
All MHTML files is not cached for IE7@Vista. Each image in mhtml re-download the entire mhtml. it’s anti-optimizations for IE7@Vista!
Marin | 03-Nov-09 at 7:52 am | Permalink |
Is there any documentation for the configuration of the mime-type of those .mhtx files?
.mhtml is defined as message/rfc822
Sharepoint’s mhtx is defined as text/plain.
Is there any doc about that extension (can’t find it on Google)
Thx!