html5shiv and Serving Content From Code Repositories

Posted: May 11, 2012 at 4:32 pm

There are a lot of interesting findings that came out of my analysis of how the Alexa Top 1000 is using HTTP compression. One finding was that JavaScript is the most common type of content served without compression. I hypothesize that this is due to websites linking to all these 3rd party JavaScript libraries and widgets. Normally a developer links to a 3rd party file to enable advertising, web analytics, user feedback and chats systems, or even social sharing widgets. But you cannot control these systems, and so those resources might not have compression enabled the way the files on your own website are enabled for compression.

Today I want to focus on a 3rd party library which wasn’t using compression because it leads to an important and often unmentioned performance lesson: Don’t link to resources inside of source code repositories.

html5shiv is a JavaScript library that enables Internet Explorer 8 and earlier to understand and properly render new HTML5 tags like <article> or <aside>. This is incredibly helpful because it allows websites to use these new semantic elements when retaining backwards compatibility. While html5shiv is open source and free to be copied and used anywhere, websites often link to a specific URL: http://html5shiv.googlecode.com/svn/trunk/html5.js.

In fact, the html5shiv article on Wikipedia includes the following source code snippet, which references this URL.

<!--[if lt IE 9]>
     <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

I would estimate that 95% of the time I witness a site using html5shiv, they are loading it from http://html5shiv.googlecode.com/svn/trunk/html5.js. Unfortunately, is this bad for performance for several reasons.

It’s Not Served Using HTTP Compression.

The reason I noticed this in the first place is that html5shiv.googlecode.com is not properly configured and does not serve JavaScript files using HTTP compression. Reducing the size of content is a well known category of performance optimization and HTTP compression is an easy way to accomplish this. (Well, easy in theory at least).

It’s Not Cached.

Google’s web server instructs the browser (and all shared caches) to only cache this file for 180 seconds. 180 seconds! By the time you are done reading this article, a copy of html5shiv sitting in your cache would have expired.

The whole point of linking to a commonly JavaScript file on a 3rd party instead of hosting it yourself is to use network effects and have that resource already be cached. That’s the entire reason that JavaScript CDNs like Google Libraries API and Microsoft’s (poorly named) Ajax Content Delivery Network exist. Of course it doesn’t always work. Regardless, 180 seconds is way too short to matter even if JavaScript CDNs were useful.

It’s Not the Most Recent Version.

This branch of html5shiv is not actively developed. As Paul Irish mentions, development for html5shiv has shifted to github, under the care of Alexander Farkas and others. You can find the most recent version here: https://raw.github.com/aFarkas/html5shiv/master/src/html5shiv.js.

(And no, don’t link to that either! It’s not minified, and the Content-Type header is wrong so, ironically, Internet Explorer will not execute the JavaScript that’s supposed to help it!)

This new version of the html5shiv has had numerous bug fixes and performance improvements. In addition, if you actually minified this new version of the html5shiv, you’d find it’s also smaller than older one on Google code. In short, the code at html5shiv.googlecode.com is obsolete.

It Says It Supports Range Requests When It Really Doesn’t.

Since we are on the subject, I might as well bring out all the issues. When you request html5shiv.js, the web server responds with an Accept-Ranges header, indicating that it supports HTTP byte ranging (also known as partial responses) as shown in the screen shot below:

This means the server supports conditional requests, allowing you to download just a small portion of the file. This is helpful when the client has already downloaded some of the response, such as when recovering from a network error. As I wrote in a previous post, supporting partial responses is good for performance. Only googlecode.com doesn’t support partial responses. I used REDBot (which is awesome) to detect this issue, and you can see the results here. If you send a request and specify a byte range using the Range header, the web server still gives you the full response.

The Biggest Problem of all

While these are important issues, they are really just a symptom of a much bigger problem: You’re linking to a source code repository.

Think about that for a second. You are directly linking to a resource in an external source code repository, which you do not control! Giant sirens and flashing lights should be going off in your head.

First of all, because this is a source code repository, the contents could be updated at any time. This is why there is not a far-future expires header. To function properly, this URL can never have a far future expires header. This is most likely the reason that byte range requests fail as well. The content may change, and, even though Last-Modified and ETags could be used, the web server may be able to determine whether it can return a subset of the (potentially new) response body. So, by the very nature of being a code repo, you have two performance optimizations that simply can never happen.

Secondly, this is a source code repository! How many times have you accidently checked in code to a repository that broke the build? I’ve done it lots of times. In fact, it’s so common, that many departments have funny forms of punishment when a developer breaks the build, like forcing them to wear an embarrassing hat or shooting them with automatic NERF machine guns (Skynet is nigh!)

(And the real reason this is an embarrassing hat? Comic Sans MS.)

Sure, this problem exists whenever you link to an external resource beyond your control. But it’s not like we are talking about linking to the Google Analytics JavaScript file here. A simple mistake or typo when using SVN and BOOM! Someone at Google didn’t just break the build, they broke the Internet.

Marco Arment, the creator of Instapaper, has a great podcast, Build and Analyze, over at 5by5. Sometimes he and Dan even talk about development, so I highly recommend it. One of the things he has advocated repeatedly on the show (including the most recent episode), is to always avoid external dependencies you cannot control. Direcly linking to a resource in someone else’s code repository is a perfect example of an external dependency that can easily be, and should be, avoided.

Finally, at the end of the day, we are dealing with a library that is 3854 bytes in length. The new version is 2337 bytes. When served with compression it’s only 1166 bytes. Why on earth are you linking to a 3rd party to serve 1166 bytes? In the time it takes your browser to do the DNS lookup on the html5shiv.googlecode.com hostname, you could have already transmitted the file! Creating an HTTP connection to a 3rd party to download 1166 bytes is just silly and wasteful.

The Moral of the Story

The moral of the story is: “never never never link to a resource inside a source code repository”. This includes your own source code repository and 3rd party ones like googlecode or github. The characteristics inherent in a source code repository are fundamentally at odds with frontend performance best practices. Breaking code in a source code repository or version control system is much easier due to its dynamic nature. Additionally, you should not be exposing internal infrastructure systems like code repositories, version control systems, or continuous integration systems to the public internet. This is highly dangerous and increases your attack surface.

The Moral of the Story (part 2)

The other moral of the store is one I’ve written about before: “Trust, but verify”.

Top search results will tell you to link to the copy of html5shiv.js hosted on Google’s servers. Wikipedia includes a code snippet which links to this file. And you might not think that’s a bad idea. After all this is Google we are talking about! “Let’s make the Web Faster” Google! The creator of awesome performance technologies like PageSpeed and mod_pagespeed and SPDY. The employer of frontend performance greats like Steve Souders, Patrick Meehan, and Mike Belshe. That Google should know what they are talking about.

Well, that Google still got 4 things wrong trying to serve you a JavaScript file that’s less than 4 kilobytes in size. Now I’m not trying to belittle or make fun of Google. My point is that “just because” it’s from Google, doesn’t mean its right. For that matter, “just because” it’s from Zoompf doesn’t make it right. People make mistakes. As the Buddha once said:

Believe nothing, no matter where you read it, or who has said it, not even if I have said it, unless it agrees with your own reason and your own common sense.

You can trust, but verify.

Conclusions

All of this was discovered because of a JavaScript file that was served without HTTP compression. You never know what even a minor performance issue will lead you to discover. Thius is where Zoompf can help you. Our performance scanner tests your web application for nearly 400 issues affecting web performance. You can get a free performance scan of you website now and take a look at our Zoompf WPO product.

Performance aspects of Google’s HTML/CSS Style Guide

Posted: April 25, 2012 at 5:42 pm

Today Google released their HTML/CSS Style Guide. While it is full of great advice to help manage a growing code base among multiple developers, I thought it would be interested to review the web performance implications of each of its recommendations. Despite being a just style guide, nearly all of its rules had performance ramifications. For each rule I will:

  • Briefly quote Google’s style guide.
  • Link to the rule for more information
  • Discuss how the rule affects performances (either positively or negatively)
  • Mention any other related performance rules, and whether Zoompf’s free or paid offering currently detects the performance issue.

Let’s get started!

Omit the protocol from embedded resources.

Omit the protocol portion (http:, https:) from URLs pointing to images and other media files, style sheets, and scripts unless the respective files are not available over both protocols. (link)

This is a great recommendation for performance. I’ve talked about the advantages of protocol relative URLs in the past. Not only do they both reduce page size, protocol relative URLs prevent you from needlessly creating SSL connections. As an added benefit, protocol relative URLs are a bulletproof way to prevent mixed content warnings.

Applicable Zoompf Checks:

  • SSL Resource on non-SSL Page
  • Excessive Absolute URLs (Same Site)
  • Excessive Absolute URLs (External Links)

Bonus Performance Tip. While this advice is about linking resources, Zoompf recommends to you relative URLs for web content. So instead of linking to http://zoompf.com/blog/some-awesome-post you would use /blog/some-awesome-post. Not only does this reduce page size, but makes it easier to transfer content to new hostnames or restructure your site in the future.

Indent by 2 spaces at a time

Indent by 2 spaces at a time. Don’t use tabs or mix tabs and spaces for indentation. (link)

More of a style rule than performance. You should be minifying your HTML when you move it into production anyway which removes unneeded whitespace and resolves this problem.

Applicable Zoompf Checks:

  • Dynamic HTML Not Minified
  • Static HTML Not Minified

Use only lowercase

All code has to be lowercase: this applies to element names, attributes, attribute values (unless text/CDATA), selectors, properties, and property values (with the exception of strings). (link)

Using lowercase in HTML makes your pages more compressible, resulting in smaller pages.

Zoompf does not check specifically for this issue but does normalize your HTML to lowercase as part of our HTML minification analysis.

Trailing whitespace

Trailing white spaces are unnecessary and can complicate diffs. (link)

A good style rule, and since the document mentions diffs, this is mainly to make internal code management easier. As I said before, post processing your HTML, or minifying it when promoting it to the production environment resolves this issue and can slightly reduce the size of a page.

Applicable Zoompf Checks:

  • Dynamic HTML Not Minified
  • Static HTML Not Minified

Use UTF-8 (no BOM)

Make sure your editor uses UTF-8 as character encoding, without a byte order mark. Specify the encoding in HTML templates and documents via <meta charset="utf-8">. Do not specify the encoding of style sheets as these assume UTF-8. (link)

Standardizing on a single encoding is a good idea, and getting ride of the BOM saves 3 bytes. However I don’t suggest using anything inside the HTML, like <meta charset="utf-8">, to specify the charset. The Content-Type header should be the only thing you use to tell the browser about the charset. Encoding inside the document what the encoding format for a document is creates a chicken or the egg situation where the browser must parse the document to figure out how to parse the document. This creates performance problems and exposes you to XSS attacks. Google’s own documentation even references Zoompf for this.

Applicable Zoompf Checks:

  • Unnecessary <meta> Character Set (http-equiv)
  • Unnecessary <meta> Character Set (charset)

Comments

Explain code as needed, where possible. (link)

Comments are absolutely important, but they should not make it into production code. They needlessly increase the size of the resource, and often expose you to security risks.

For things like CSS and JavaScript, minification will remove the comments for your automatically. For HTML, especially dynamically generated HTML, consider using the code comments in the server-side language instead of HTML comments.

So instead of this:

    ...
    <a href="/public/">public</a>
    <!--
    Feature is not ready yet, disable until next release cycle
    <a href="/public/">public</a>
    -->
    ...

Replace it with something like this:

    ...
    <a href="/public/">public</a>
    <?php
    /*
    Feature is not ready yet, disable until next release cycle
    <a href="/public/">public</a>
    */
    ?>
    ...

Applicable Zoompf Checks:

  • Bloated HTML Tag (<comment>)
  • Excessive HTML Comments
  • Commented Out HTML
  • Dynamic HTML Not Minified
  • Static HTML Not Minified
  • JavaScript Not Minified (External File)
  • CSS Not Minified (External File)
  • JavaScript Not Minified (<script>)
  • CSS Not Minified (<style>)
  • Feed Not Minified

Action items

Highlight todos by using the keyword TODO only, not other common formats like @@. Append a contact (username or mailing list) in parentheses as with the format TODO(contact). (link)

TODOs are a quick way to remind you of additional tasks. Just make sure that TODOs are not included in content sent to the user. Since TODOs are placed inside of comments minification fixes this issue.

Zoompf does not check for TODO items specifically, but this issue will be flagged more generically by Zoompf comments and minification checks.

Use HTML5

HTML5 (HTML syntax) is preferred for all HTML documents: ‘<!DOCTYPE html>’. (It is recommended to use HTML, as ‘text/html’. Do not use XHTML. XHTML, as ‘application/xhtml+xml’, lacks both browser and infrastructure support and offers less room for optimization than HTML.) (link)

Smaller DOCTYPE means smaller bytes. XHML is also more bloated than HTML, requiring you to use closing tags and <CDATA> which [needlessly increases the size of your page](http://perfectionkills.com/optimizing-html/). Avoid XHTML, especially on mobile sites.

Zoompf does not currently check for HTML5 use, or mobile sites using XHTML.

Use HTML according to its purpose

Use elements for what they have been created for. (link)

Google’s example for this rule shows a <div> using a onclick to replicate a standard <a href=""> tag. Another common example is that developers with use an <a> tag with an empty href attribute, but use an inline onclick handler which simply navigates to a web page.Reinventing the wheel like that almost always results in larger markup, increase the size of the page.

Zoompf does not check for using elements properly.

Separate structure from presentation from behavior

Strictly keep structure (markup), presentation (styling), and behavior (scripting) apart, and try to keep the interaction between the three to an absolute minimum. (link)

Placing JavaScript and CSS into separate external files allows you to reuse common content and reduce the size of all of your pages. These external files can also be cached while HTML often cannot.

This is another good example of advice that is good for development and not good for production. Despite the guide advice, Developers can and should separate code into files as it logically makes sense, such as separating libraries. The same is true for CSS. However, you should not reference 8 JavaScript files in the HTML when the website moves to production. Those files should be combined and versioned as part of the publishing process.

Bonus Tip. Smaller files should be inlined into the document. Software like mod_pagespeed can do this automatically.

Applicable Zoompf Checks:

  • Common Style Attribute
  • Common Content (JavaScript Block)
  • Common Content (Style Block)

Do not use entity references

There is no need to use entity references like &mdash;, &rdquo;, or &#x263a;, assuming the same encoding (UTF-8) is used for files and editors as well as among teams. (link)

All of these symbols appear in UTF-8, so there is no reason to use HTML entities, reducing the size of your page.

Zoompf does not currently check for using entity references on UTF-8 encoded pages.

Omit optional tags (optional)

For file size optimization and scannability purposes, consider omitting optional tags. (link)

I disagree with this advice. While it makes sense to omit end tags of elements that have no meaningful inner HTML (<meta>, <link>, <img>, <script src="">), you are just asking for maintainence issues if you start omitting things like </p>. In fact, Google’s example of recommended HTML usage is actually a better example why this is a stupid practice.

If you truly want to do something like this, don’t try to do it in your source HTML or template files. Add these aggressive HTML minification optimizations as part of your publishing process when moving a website to a production environment.

Zoompf does not check for optional end tags being used.

Use valid CSS where possible

Unless dealing with CSS validator bugs or requiring proprietary syntax, use valid CSS code. (link)

CSS minification tools are very primitive. While JavaScript minifiers like YUI Compressor or Closure Compiler actually tokenize, parse, and build syntax trees to guide minification, CSS minifiers use regular expressions or primitive lexers. Invalid CSS often breaks these tools. Make sure your CSS is valid.

While Zoompf does not check for invalid CSS syntax, it does check to make sure that CSS resources referenced with a <link> tag or @import statement are, in fact, CSS files.

Use appropriate lengths for ID and class names

Use ID and class names that are as short as possible but as long as necessary. Try to convey what an ID or class is about while being as brief as possible. (link)

Shorter names are better than long names for performance. Zoompf does not currently check for long ID or class names.

Avoid qualifying ID and class names with type selectors

Unless necessary (for example with helper classes), do not use element names in conjunction with IDs or classes. (link)

In other words, ul#ref-list is not needed when #ref-list will do. This has three performance implications. First, simplifying how CSS selectors match speeds up rendering. Additionally, the rule is more generic, allowing it to be reused in more situtations and prevents the creation of redundant rules. Finally, smaller selectors mean smaller CSS files, which is always good for performance.

Zoompf does not currently check the specificity of CSS rules.

Omit Default or Unneeded Values

Google’s document lists a number of rules for omiting default or unneeded values from CSS. All of these rules lead to smaller CSS files, which is a good thing for performance. All of these rules can be automatically fixed with CSS minification.

Applicable Zoompf Checks:

  • CSS Not Minifed (External File)
  • CSS Not Minifed (<style>)

Conclusions

Every organization should use a style guide. For the most part, Google’s HTML/CSS Style Guide promotes good style advice that is also good performance advice, making it a good standard to use. Just remember, you should not make things easy for developers and the expense of your visitors. At the same time, don’t force a style on the developers that makes code or content harder to undestand for the sake of performance. Instead, use a build process to apply many of these optimizations automatically.

Want to see what performance problems your website has? I’ve mentioned only a few of the nearly 400 performance issues Zoompf detects when testing your web applications. You can get a free performance scan of you website now and take a look at our Zoompf WPO product at Zoompf.com today!

How Do Google’s Animated Doodles Work?

Posted: April 23, 2012 at 6:08 pm

Sunday was Earth Day. As with many holidays or anniversaries, Google celebrated Earth Day with an animated doodle. This is certainly not the first animated doodle that Google has done. Since I have been speaking so much about images recently, I thought it would be interesting to see how Google creates small and fast animations. The solution surprised me, and changed my mind about animated images on the web.

What Google is doing

The Google Earth Day animation is a non-repeating linear sequence of images. Unlike previous animated doodles, it is not interactive. The animation is 486 pixels wide by 182 pixels tall. Traditionally, this could be accomplished using an animated GIF image. In fact, that is what I thought they were doing at first. However Google took what I think is a different and better approach.

Instead of an animated GIF Google stored all the animation frames vertically inside a single JPEG image that is 468 pixels wide by 2912 pixels tall. There are 16 frames of animation stored this way, somewhat resembling a strip of film, as shown in the screen shot below:

filmstrip image opened in the GIMP

This is kind of like a CSS sprite, but each image is a frame of animation. To clarify things, I will call this animation technique filmstrip animations. Another animated doodle which uses filmstrip animation is this Gumby logo from October 2011.

So how does Google make the filmstrip animation work? First they enclose the <img> tag JPEG with all the animation frames inside a <div> tag that has a fixed height of 182 pixels and which hides overflow. This creates a fixed window so to speak, which masks all but current animation frame. The image is animated using JavaScript, which changes the top property for the absolutely positioned image to slide it up a fixed interval with the setTimeout() function. A diagram of this concept is shown below:

Diagram of how filmstrip animation works

The code for this looks something like this:

<div style="height:182px;position:relative;width:468px;overflow:hidden">
	<img border="0" src="source.jpg" id="filmstrip" style="position: absolute; height: 2912px; top: -0px; display: block; ">
</div>
<script>
function naiveAnimation(id) {
	var img = document.getElementById(id);
	var offset = 0;
	var animate = function() {
		//slide the image correct frame of animation given by  offset
		img.style.top = -offset + "px";
        //calculate offset to next frame
		offset = Math.floor(offset + 182);
		//if we are not yet on the last frame...
		if(offset < 2912) {
			//call me again in half a second
			window.setTimeout(animate, 500);
		} else {
			//at last frame, so all done!
		}
	};
	//start the animation
	animate();
}
naiveAnimation('filmstrip');
</script>

However, this animation is jerky compared to Google’s Earth Day. In Google’s animation, the frames seem to fade into each other. How is Google doing this?

Generating Intermediate Frames

Google uses a neat trick. They use opacity to fade between two different frames of animation and create, in effect, additional intermediate animation frames. This makes the animation look smoother.

To accomplish this, Google uses two <img> tags inside of the container <div> and uses absolute positioning and z-index to place them directly on top of each other. You can see this clearly using Firefox’s 3D DOM viewer, as shown in the screen shot below:

Firefox's 3D DOM Viewer

Each image is pointing at the filmstrip image. Let’s call the “lower” image tag <img> #1 and the “upper” image tag <img> #2. To accomplish the effect, <img> #1′s top property is set to show animation frame 1 and <img> #2 is set to show animation frame 2. The opacity property of <img> #2 is set to 0, making it full transparent so animation frame 2 is not seen. This setup is shown in the diagram below:

Fading animation effect explained

Google then uses a fast JavaScript timer to increase the increase the opacity of <img> #2 every 50 milliseconds. Increasing the image’s opacity makes it less transparent, so more and more of <img> #2 is visible and less of <img> #1 is visible. This makes animation frame 2 fade in on top of animation frame 1, as shown in the screen shot below:

Fading animation effect explained (part 2)

Google slowly fades the image in over 10 steps of 50 milliseconds. After 500 milliseconds:

  • <img> #2, displaying animation frame 2, is fully opaque and covers <img> #1.
  • The top property of <img> #1 (which is now hidden by the fully opaque <img> #2 ) is increased so that <img> #1 now points at animation frame 3.
  • JavaScript is now used to decrease the opacity of <img> #2 every 50 milliseconds This means <img> #2 (displaying animation frame 2) slowly becomes transparent, allowing <img> #1 (now displaying animation frame 3) to fade into view.
  • The process repeats

In short, using two images, changing opacity up and down, and moving the position of the two copies of the same filmstrip images, Google can create an animation with fading transitions between frames!

But what about animated GIFs?

My initial thought was that Google should be using an animated GIF. Why didn’t they? To find out I needed to create a GIF animation of the same content as the Earth Day filmstrip animation and compare the two. I took the filmstrip JPEG and sliced it into 16 separate images using Imagemagick’s convert -crop command. To create the animated GIF, I used GIMP, inserting each animation frame as a separate layer of a single image. I then exported it as an animated GIF with 500 milliseconds between frames. The resulting animation is shown below:

Google Earth Day animation as an animated GIF

The animated GIF approach was bad for several reasons.

  • GIF’s are not ideal at storing photographic images and all the frames of the animation must share the same palette of 256 colors.
  • The resulting GIF image is huge at 980 KB! GIF animations can be optimized to store the differences between frames. However, since my source image was a JPEG, the color values for pixels vary between each frame. In other words, the color value for the dirt color in the top left corner of each animation frame is different. This means there is little commonality between frames, so more graphics data must be stored for each frame, resulting in a large image. Since the original sources of each animation frame were photographs taken of a 3 dimensional biological scene (with different lighting effects and shadows, etc), I think it’s unlikely that having access to the original source images would have allowed me to create a more optimized animation.
  • The animation is jerky. It is only 16 frames and after one complete frame is shown it is replaced with next complete frame 500 milliseconds later. The animation runs at 2 frames per second.

Contrast this with Google’s JPEG filmstrip approach:

  • The image is a JPEG, allowing millions of colors and photographic realism.
  • The JPEG animation is only 270 KB, plus a few hundred bytes of HTML and JavaScript.
  • Google’s animation is much smoother. Even though the JPEG contains only 16 frames, the fading transition between frames occurs in 10 “steps.”. This essentially creates a 160 frame animation running 20 frames per second. Our animated GIF would grow significantly larger to create this same effect.

A New Animation Format?

Animated transitions have been popular with image galleries and carousels. However, the intended effect was not to create a single smooth piece of animation. By coupling tradition transition animation with a filmstrip of animation frames, Google has created a new and highly adaptive animation format for the web which is supported by every modern browser. A single image is used to store the animation frames and JavaScript advances the frames and drives addition effects. Consider the benefits of filmstrip animations:

  • Near universal compatibility. No new image format needs to be supported.
  • Use the best image format for the job. Need photorealism? Use a JPEG. Need an animation with alpha transparency? Use PNG! GIF animations cannot adapt like this.
  • Reduced size. The number of animation frames can be reduced by using JavaScript to generate intermediate frames. In our example, Google used opacity for fading, but this is just one method. Wipes of any kind are possible, in addition to moving specific parts of the image.
  • JavaScript can adapt to the platform. Look at navigator.network.connection and see you are on a slow network? You can respond and fetch a lower quality filmstrip image, or one with fewer animation frames.
  • Better control over frame order. With animated GIFs, you move from the first to the last frame, and then optionally start over again at frame 1. With JavaScript you can go from the start to the end, and then backwards for the end to the start. This allows you to store symmetrical animations in half the space. Or you could revisit only certain frames. This is more flexible, allows for better transitions, and avoid sharp jumps when the animation repeats.

But it requires JavaScript!

No, not really. You can still have an animated GIF inside of a <noscript> tag like this:

<div id="filmstrip-con">
	<script>
	document.write("<img src='filmstrip.jpg' id='filmstrip-img'>);
	<noscript>
	<img src="animated.gif">
	</noscript>
</div>

(Yes, document.write is a bad idea here, but I’m using it for brevity.)

This is a big win all around. If even only 10% of your visitors have JavaScript enabled, that is 10% of visitors who don’t need to download a 1 megabyte GIF image. Anyone who doesn’t have JavaScript enabled can still see the animation, it will just load slower and potentially provide a lesser experience than the filmstrip animation.

But Why Not a CSS Animation?

If GIF animations are one extreme, CSS animations are at the other end. I don’t like CSS animations for a few reasons. The first is that browser compatibility, especially backwards compatibility is lacking. But the biggest is that the client is the one creating the animation. This is because CSS animations are extremely granular and microscopic. “Move this element in this way, at this time. Move this other element a different way at a different time.” With animated GIFs or the filmstrip approach, the content of the frames and how it changes are generated by the author, and a series of static images are presented to the client to draw.

With CSS animations, the animations are created on the client, so the computing power of the device greatly affects the experience. If the browser does not support hardware accelerated CSS transforms CSS animations are jerky and provide a poor experience. Additionally, all that microscopic “Move X to Y” makes creating and maintaining and updating animations difficult. While the filmstrip approach is more involved than simply exporting an animated GIF, it is clearly easier than composing an animation from DOM elements and coordinating their movement.

Filmstripper: A naive implementation

If you’d like to use filmstrip images, I’ve create a small proof of concept, filmstripper, that abstracts away specifics for you. The focus is on being as unobtrusive as possible. That means no external library dependencies and minimal markup or required tag decorations. Even though it’s fairly primitive (I haven’t added Google’s fading effect yet) this approach works well. If JavaScript is not enabled or an error occurs, a static image is shown. This allows animation to be added responsively.

To use filmstripper, create a filmstrip image with all the animation frames arranged vertically. Next, create a static image just containing the first frame of the animation. The code HTML is minimal:

<div id="filmstrip-con">
	<img src="static.jpg" id="filmstrip-img">
</div>

To animate, just call filmstripper(). The code is below:

/*
 * filmstripper - Simple filmstrip based animations
 *
 * contID - ID of DIV container element
 * imageID - ID of IMG element to animate
 * stripImageUrl - URL to the filmstrip image (frames arranged vertically)
 * delay - delay in milliseconds between frames
 */
function filmstripper(contID, imageID, stripImageUrl, delay) {
	var con = document.getElementById(contID);
	var img = document.getElementById(imageID);
	var frameHeight = img.height;
	var throwAway = new Image();
	var setup = function() {
		//setup the div to hide the strip
		con.style.cssText = "height:" + frameHeight + "px;position:relative;width:" + img.width + "px;overflow:hidden";
		//setup the image to scroll frames, and switch static to strip
		img.style.cssText = "position: absolute; height: " + throwAway.height + "px; top: -0px; display: block;";
		img.src = throwAway.src;
		var d = 0;
		animate = function() {
			img.style.top = -d + "px";
			d = Math.floor(d + frameHeight);
			if(d < throwAway.height) {
				window.setTimeout(animate, delay);
			}
		};
		animate();
	};
	//request the film strip image
	throwAway.src = stripImageUrl;
	//launch everything if it loads
	throwAway.onload = setup;
}

Conclusions

Animated GIFs, while simple and widely supported, have several short comings. Google uses a single image containing arranged as a filmstrip and JavaScript to advance to animation frames. This approach allows for better, smoother animations which require less content to be delivered to the browser. If you have large animated GIFs, consider replacing them a filmstrip animation instead.

Want to see what performance problems your website has? Zoompf can analyzed your website for nearly 400 issues which affect web performance and load time. You can get a free performance scan of you website now and take a look at our Zoompf WPO product at Zoompf.com today!

Should You Use JavaScript Library CDNs?

Posted: January 15, 2010 at 1:40 pm

The concept is simple. Hundreds of thousands of websites use JavaScript libraries like jQuery or Prototype. Different websites you visit each download another identical copy of these libraries. You probably have a few dozen copies of jQuery in your browser’s cache right now. That’s silly. We should fix that.

How? Well, if there was a 3rd party repository of common JavaScript libraries, websites could simply load their JavaScript files from them. Now imagine the repository implemented caching. SiteA, SiteB, and SiteC all have <SCRIPT SRC> tags that reference http://some-code-respo.com/javascript/jquery.js. When someone visits any one of these sites, the JavaScript library jQuery is downloaded and cached. If that same person visits one of the other sites, that person will not have to re-download jQuery again. The idea is that sites will load faster because these libraries should not have to be re-downloaded very often at all. Of course, this only works if a lot of people all use the common repository. If only a few people use the common repository, then virtually no one benefits because the library will not have been downloaded and cached by a previous website and has to be re-downloaded.

This is an example of the Network effect. The more people that use a system the more valuable the system becomes.

Implementations of this idea of a central shared repository of common JavaScript libraries are called several different things. Google calls their implementation Google AJAX Library API. Yahoo doesn’t have a clear name for their implementation. I’ve seen “Free YUI hosting” or “YUI Dependencies”, or even Yahoo YUI CDN. Microsoft calls their implementation the Microsoft AJAX CDN. To keep things simple, I will collectively refer to these repositories of common JavaScript libraries as JavaScript Library CDNs.

JavaScript Library CDNs seem like a performance no brainer. Use the service, your site loads faster and consumes less bandwidth. This post will explore if and under what conditions does a JavaScript Library CDN actually improve web performance.

The Choice

Consider this situation. You are speed conscious web developer. You have a website that uses jQuery 1.3.2 as well as some additional site specific JavaScript. Because you value web performance, you know you should concatenate all your JavaScript files into as few files as possible, minify them, and serve them using gzip compression. You have 2 choices:

  1. Serve all your JavaScript locally. You will have a single <SCRIPT SRC> tag that points to a JavaScript file containing jQuery 1.3.2 and your site specific JavaScript.
  2. Serve some of the JavaScript using a JavaScript Library CDN. You will have 2 <SCRIPT SCR> tags. The first tag will point to a single file on your website containing your site specific JavaScript files. The second tag will point to the copy of jQuery 1.3.2 on Google AJAX Library API.

What’s the difference? Well a minified, gipped copy of jQuery 1.3.2 is 19,763 bytes in length. If you choose option 1 all your users will have to download these 19,763 bytes regardless of what other sites they may have already visited. That’s the cost: downloading 19,763 bytes. Notice there is no cost of an additional HTTP request and response or other overhead because those bytes of jQuery content are included inside the response for the site specific JavaScript content which the visitor already has to make. This is important, so I will repeat: The cost of not using a JavaScript Library CDN is only the downloading of JavaScript content and not any additional HTTP requests or overhead.

In the second option, you are going to gamble with a JavaScript Library CDN. You are hoping a visitor has already browsed another website which also uses Google to serve jQuery 1.3.2. If you are right, then that visitor does not need to download 19,763 bytes. If you wrong, the visitor needs to download 19,763 bytes from Google. That’s the prize in a nutshell. And downloading 19,763 bytes doesn’t sound bad! Who cares where it comes from?

The Price of Missing

Unfortunately an HTTP request to Google’s JavaScript Library CDN is more expensive than an HTTP request to your own website! This is because a visitor’s browser has to perform a DNS lookup for ajax.googleapis.com and establish a new TCP connection with Google’s systems. If the additional request was to your site instead the visitor’s browser would not need to make another DNS lookup and the HTTP request would be sent over an existing HTTP connection.

Unfortunately this is a stubborn process. DNS lookups and establishing TCP connections involve a few number of very small packets. Having a faster Internet connection will not significantly impact the speed of these operations. Two different runs on WebPageTest showed that it takes 1/3 of a second for a web browser to make a connection to Google’s JavaScript Library CDN and start downloading it. (And remember, these are CDNs so where I make the request from should not matter as the CDN makes sure I’m downloading the content from a web server that is geographically near me.)

Let me repeat that: Using Google’s JavaScript Library CDN comes with a 1/3 of a second tax on missing. (Note that a tax like this applies to opening connections to a any new host: JavaScript Library CDNs, advertisers, analytics and visitor tracking, etc. This is why you should try to reduce the number of different hostnames you serve content from.) Even if this number is smaller for other users, say, 100 milliseconds, it is still a tax that is paid for using a JavaScript Library CDN and missing.

It gets worse because downloading a file over a new TCP connection with Google is slower than downloading a file over an existing TCP connection with your website! This is due to TCP’s slow start and congestion control. Newly created connections transmit data slower than existing connections do. (This is why persistent connections are so important!)

The Odds of Winning

Since JavaScript Library CDNs utilize the Network Effort, they are only valuable if a large number of websites use them. After all, the only way your visitors can “win” in the JavaScript Library CDN gamble is if they have already been to a site that also uses the same CDN. So, how many people actually use Google?

Well, according to the great folks at BuiltWith, only 13% of all websites use some kind of 3rd party CDN. Of those websites using a CDN, 25.56% of them are using Google’s Ajax Library API. So only 3.89% of all websites surveyed are using Google’s AJAX Library API.

I wanted to gather more data than BuiltWith. I also didn’t like that way they grouped Traditional CDNs (like Akamai) with JavaScript Library CDNs (like Google) with private site-specific CDNs (like Turner’s CDN). So I performed my own survey. I visited the top 2000 sites on Alexa and analyzed each one to see who is using Google’s JavaScript Library CDN. The result? Only 69 sites out of 2000, or 3.45%, are using Google’s JavaScript Library CDN. My data is on track with BuiltWith’s data which is good.

Unfortunately you do not vaguely or abstractly “use a JavaScript Library CDN.” You reference a specific URL for the specific JavaScript Library and version number. You only get a benefit from the CDN if you referencing the specific URL that other websites are referencing. So we have to dig deeper and see what versions of what JavaScript libraries are in use. Below is the a table of JavaScript libraries that Alexa Top 2000 sites use served by Google’s AJAX Library API.

JavaScript LibraryNumber of Alexa Top 2000
sites serving the library
from Google’s CDN
jQuery48
Prototype6
SWFObject6
YUI6
jQuery UI4
Script.aculo.us3
MooTools3
Dojo1

We see that 48 sites are using Google’s JavaScript Library CDN to serve jQuery, and of those 36 sites are using jQuery 1.3.2. That means jQuery 1.3.2 is used by 1.8% of the Alexa 2000 websites. SWFObject and Prototype came in next at 6 sites each, or less than 0.334% of the sites. When you factor in version numbers, their penetration drops to around 0.10%.

So what is the best case here? What are the odds that someone would have jQuery 1.3.2 served from Google’s JavaScript Library CDN sitting in their browser cache? If I have clear browser cache, and I visit 35 randomly selected websites from the Alexa top 2000, and then I visit your site, there is only a 47% chance that I will have a cached copy of jQuery 1.3.2 ready for you to use. You calculate this by first determining the probably of randomly picking 35 websites that don’t have jQuery 1.3.2 and subtracting 1. The formula is: 1 – ( (1 – .018) ^ 35 ).

Those are not very good odds. And they only are applicable if you are using jQuery 1.3.2. Anything else is not practical. You also should consider the makeup of the sites on the list. I have probably only visited 30 or so of the websites listed in the Alexa top 2000 list ever and I probably only visit 5-10 with any regularity. We have determined that the odds of “winning” in the CDN gamble are fairly small. How small the odds are will depend on your site content and your visitors. However I think it is safe to say, as of January 2010, the majority of your users will not have visited a site that uses a JavaScipt Library CDN for the JavaScript library that you use.

Getting More Data

So maybe the odds aren’t good. But is it still worth it to potentially help some people?

Let’s go back to our hypothetical situation where we are deciding if we should use a JavaScript CDN or not. Consider someone with 768 kilobyte per second Internet connection where 768 * 1024= 786,432 bits downloaded per second. Let’s say it is operating at only 80% efficiency to account for overhead like IP, TCP, congestion, packet loss, etc. That 629,145 bits downloaded per second, gives us 78,643 bytes downloaded per second or 26,214 bytes downloaded in 1/3 of a second. A minified and gzipped copy of jQuery 1.3.2 is 19,763 bytes long. This means anyone using a 768 kbps internet connection can download the contents of jQuery 1.3.2 in 1/3 of a second. In other words, downloading jQuery 1.3.2 on that connection takes the same amount of time as simply connecting to Google’s JavaScript Library CDN.

This simplifies the decision in our hypothetical situation on where to host jQuery. In the locally hosted option, we are asking our visitors to download some amount of content X. X is all our HTML, images, site specific JavaScript, and includes the 19,763 bytes of jQuery 1.3.2. In the “use a CDN” option, we still have X amount of content. The only difference is the CDN has the 19,763 bytes of jQuery and our site has X – 19,763 bytes of content. If a visitor does not have cached copy of JavaScript Library they still download a total of X amount of content. It is served from our website and from Google. Under these conditions we are led to the following points:

  1. If you are using a CDN and the visitor does not have cached copy, they download the site 1/3 of a second slower than if they had downloaded all the content from your web server.
  2. If you are using a CDN and the visitor does have cached copy, they download all of the content 1/3 of a second faster than if they had downloaded all the content from your web server.

Or, more simply: If we use Google’s JavaScript Library CDN, we are asking the majority of our website visitors (who don’t have jQuery already cached) to take a 1/3 of a second penalty (the time to connection to Google’s CDN) to potentially save a minority of our website visitors (those who do have a cached copy of jQuery) 1/3 of a second (the length of time to download jQuery 1.3.2 over a 768kps connection).

That does not make sense. It makes even less sense as the download speed of your visitors increases. Try to avoid serving 20 or 30 kilobytes of content at the cost of using a 3rd party just doesn’t make sense.

Conclusions

JavaScript Library CDNs use the network effect. Our survey of the Alexa 2000 shows that right now there are too few people in the network to get any value. Only Google’s AJAX Library API has anywhere near the penetration to provide any benefit and only if you are using a specific version of a single JavaScript library. Even in that remote case, serving jQuery 1.3.2 using Google will slow down the majority of your users at the expense of a possibly nonexistent minority. Zoompf recommends the vast majority of websites avoid using JavaScript Library CDNs until they gain more market penetration.

I will discuss the very select group of sites that should use CDNs, as well as some other interesting data discovered while surveying the Alexa 2000 in posts early next week.

Want to see what performance problems you have? Using JavaScript Library CDNs appropriately are just a few of the 200+ performance issues Zoompf detects while assessing your web applications for performance. You can sign up for a free mini web performance assessment at Zoompf.com today!