Last week Yahoo! launched Earth Day campaign with a dedicated page, plus other campaign features spread across Yahoo! properties. Being the frontend engineer in charge of implementing such features for Yahoo! Search Team and a tree-hugger, I had to find a way to go green on this task. So, why not optimize it to the bone?
The page itself is pretty light and hasn't many features besides the main search box and some tab links. So the Earth Day module would come as extra weight to the page.
The following screenshot of the Earth Day campaign module on Yahoo! Search shows the original image. (You might be reading this after the campaign is gone or has changed).
- 9 different images (photos): JPG
- 1 background image (green leaf): transparent PNG8
- 1 background gradient (from white to green): PNG8
With these image resources, I did some performance homework and strictlyfollowed the steps outlined in Even Faster Web Sites book by Stoyan Stefanov and Nicole Sulivan, to optimize images to the minimum size and best quality possible. So I ended up having optimized images — the first performance gain — and had to find a way to serve those images efficiently.
This was the second performance gain. Stoyan had already posted how to achieve performance with fewer images, Since the module header gradient is vertical linear from white to green, I was able to use:
- CSS3 where supported (Gecko and WebKit)
- filters on IE
- a fallback background color for unsupported browsers and progressive enhancement
These solutions saved the day, as I ended up with one less HTTP request. To choose the fallback background color, I picked the average color between the gradient range, resulting in a greenish hue that contrasts properly with module header text and image (dark green leaf).
So far, this resulted in only two gains. I had to pursue the best way to serve those optimized images: 3 out of 9 random Earth Day images plus the green leaf on the module header.
First approach: Serve image as is
This seems too lame, performance-wise: 4 extra HTTP requests sound bad to my ears. Although it's cross-browser compatible, with easy implementation and low maintenance, I skipped it.
Pros: straightforward implementation, low maintenance, browser caching
Cons: too many HTTP requests
Second approach: Sprites
Sprites sounded like a good candidate. But since only 3 images are displayed at a time and I had 9 different images plus the transparent module header (green-leaf image), sprites would not work for the following reasons:
- For one giant sprite, the final file size would be too large for the number of displayed images on the page.
- The module header image is an optimized PNG8 with transparent background and the 9 images are JPG, so they couldn't be combined.
- Doing all possible combinations of images to display 3 at a time into different sprites: 9!/(3!*(9-3)!) = 84 different sprites — this would be a waste of host space.
Pros: 1 HTTP request, browser caching
Cons: File too large/too many files, no transparency for JPG+PNG8
Third approach: Lazy load images
Similar to the first approach; however, no images would be served until the page is loaded (
window.onload event). This approach only delays the problem of extra HTTP requests to the end, serving all content to the user first then the image resources once the page is loaded. This is not all bad but there was more room for improvement.
Pros: Serves content to user right away by delaying image loading, browser caching
Cons: Too many HTTP requests
Fourth approach: External CSS with Data URI
This creates the same problem as having 1 giant sprite or 84 combinations of images: the former would have 1 giant CSS file serving 9 possible images plus the header image, but only 3 images would be displayed at a time and the latter would be hosting too many CSS files on the content delivery network (CDN).
One good point here is that it would be cached, so users would be able to take advantage of one less HTTP request on next visits for one single sprite solution. This is not true when using all possible combinations solution. I'll explain later in this post why caching wasn't an issue. This is also cross browser-compatible, with a few edge cases on Internet Explorer (IE) that would make the file double its size. Check out Stoyan's
post about that.
Pros: 1 HTTP request, browser caching, solves the problem of combining JPG + transparent PNG8
Cons: File too large/too many files, IE (Vista/Win7) issues
Fifth approach: Combo-handling external CSS with Data URI
Very interesting solution, having 10 CSS files (9 photos + green-leaf header image) containing image data URI, then combining them using a combo handler. This solves the problem of all possible combinations since it would be made on-demand, but there are some drawbacks:
- There is server overhead to concatenate those files for each request.
- To serve IE versions earlier than version 8, MHTML is needed. This doubles file size and makes it impossible to concatenate, preserving the header comments
without splitting them. Read more about it on Stoyan's post.
Even with the benefit of browser caching, this wouldn't be that helpful, because every new visit could bring a new combination. So users would cache up to 84 possible CSS files. But the main reason for not using this approach is because the CDN where the CSS would be hosted has no such combo handler installed.
Pros: 1 HTTP request, browser caching, solves the problem of all possible combinations
Cons: Server overhead per request, too many files to be cached, IE pre-version 8 issues, not available on all CDNs
Last approach: Data URI for image source
Pros: No HTTP request
Cons: No support for IE pre-version 8, no browser caching
The main disadvantage of serving everything inline is the lack of cache when everything comes embedded in the page. You could use this benefit if you were using an external CSS with data URI or using sprites but, since Earth Day is a temporary campaign, the nature of users' access is not supposed to be cached. So the problem here was that the page has no external CSS: everything is already being served inline. Serving either an external CSS or a sprite would add extra HTTP request.
The graphs below show the network waterfall charts for the same page, where the first one (all good browsers) has no requests for Earth Day module resources and the second one (IE pre-version 8) has 4 HTTP requests for these resources.
As you can see, fewer resources to download means less HTTP-request overhead, resulting in ~600 milliseconds of savings using the data URI approach.
The performance gains here focus on user experience and perception, by serving the page as fast as possible. For this reason, increasing the page size (data URI for images) and not adding extra http requests made it possible. Obviously, image optimization was mandatory, the smaller the image gets, the smaller the data URI length gets too.
Go green! Save trees, save bandwidth, help reduce wire traffic overhead: Optimize your pages too!
Happy Earth Day!
Liked what you read? Yahoo! Search Team is hiring! If you are crazy about performance/innovation and would like to make an impact on every 1 out of 2 people online, see our currently available positions. This post reflects only part of our everyday decisions when dealing with performance to benefit our users. Be part of this brilliant team.