What, you mean to tell me you’re not already loading your scripts with the new hot thing, LABjs?
Just kidding, you’re probably just now starting to hear about it. But let me tell you, it is the next big thing. I mean, when was the last time someone found a way to completely revamp the plain ol’ boring <script> tag and give it new life!?
All levity aside, LABjs stands for Loading And Blocking JavaScript. It’s a general purpose script loader that aims to be able to effectively load any script resource(s), from any location, into any page, at any time. It loads them all as parallel as the browser will allow, but maintains execution order when you express the need to do so in the usage of the API, for keeping dependencies safe.
LABjs’ primary goal is to replace the “<script> tag soup” in your pages (you know, all that garbage that clutters up your <head> or the end of your <body>) with a simple and expressive API that gives you complete control over the loading and executing behavior of your scripts.
LABjs 1.0, just released yesterday, now sports the “preloading” feature, which is the culmination of several months of collaborative efforts with Steve Souders to bring the best of his script loading work from his most recent book Even Faster Web Sites (EFWS) together with the expressive power of LABjs. We hope the new release represents the best of both worlds, and a really strong, solid solution for loading JavaScript resources into your pages.
What is “preloading” for?
The primary idea behind the “preloading” feature is to “look ahead” in the chain of LABjs API calls and start loading all (or, strictly, as many as the browser will allow at a time) scripts at once, but doing so in a “preloading” way much like was common years back with preloading images into the cache via JavaScript techniques.
Since the scripts are only preloaded, and not actually executed, LABjs is free to control the execution order of the scripts, which allows it to preserve proper execution order if you use the API functions to express the need to “wait()” for dependencies.
So, how does this improve loading of my page?
In addition to making the scripts load in parallel with each other (as much as possible), the scripts also load in parallel with the rest of the page’s resources. This means that virtually all page resources will load at nearly the same time, rather than waiting for one to finish before the next starts, as is common in many current browsers’ implementations of <script> tag behavior.

FF3, loading scripts serially with each other and page resources. 16.84s
Notice how the 3 script files load in succession, and only after they complete do the images start to download (at least those download in parallel!). 16.84 seconds to download serially.

FF3.5, loading scripts in parallel with each other, but still blocking page resources. 10.69s
FF3.5 introduced a nice new behavior in that they will download scripts in parallel, but still maintain execution order. So, in this diagram, we see that the scripts are parallel to each other, but still they block other page resources. Down to 10.69 seconds loading all resources.

LABjs, loading scripts in parallel with each other and other page resources. 6.24s
This diagram shows the loading behavior of LABjs in virtually all browsers, modern and not-so-modern. Notice that not only are scripts loaded in parallel to each other (while still maintaining execution order when necessary), but in parallel with other page resources as well. This means that you can use a much larger slice of the browser’s bandwidth to grab page resources as quickly as possible, speeding up the loading of the page by significant amounts (generally 2x to 3x speed increase).
In this example, we went from 16.84s down to 6.24s. That’s a 2.7x speed increase. Now that’s something to get excited about!
Are there any negative side effects?
LABjs (and other dynamic script loaders like it) are so good at getting page resources and scripts to load more quickly, it unintentionally creates a lesser seen phenomenon in web user-experience that I am calling “FUBC” (flash of un-behaviored content) — pronounce it “fubik” — which is a takeoff of the more commonly known FOUC (flash of un-styled content).
FUBC means that it is possible that your raw (but still CSS styled) content may arrive to a page and even be displayed momentarily before your JavaScript has had a chance to kick in and attach behaviors or modify it. For some sites, the changes made by JavaScript behaviors are subtle and most users won’t notice. But for other sites, the “flash” between the two views of the content is much more drastic and jarring.
Because this can be a potentially disruptive user-experience, I suggest the following simple steps to address the FUBC phenomenon:
- In your main CSS of your page, hide (display:none or visibility:hidden) all content which will have drastic re-rendering by your JavaScript.
- In your JavaScript, make sure to re-display/unhide the content once it has been styled by your code.
-
To take care of users who do not have JavaScript enabled, put a <noscript> block in the <head> of your page that unhides any
content hidden by your main CSS — essentially, undo the CSS hiding of content, since there won’t otherwise be any JavaScript to do so.
By taking these 3 simple steps, you will effectively address the disruptive FUBC user-experience for JavaScript-enabled users, while still preserving proper display for the non-JavaScript crowd.
Anything else?
There are a couple of minor caveats where LABjs may not be appropriate for your site or scripts. One specifically you should be aware of is the implications of LABjs with DOM-ready and frameworks like jQuery.
But overall, there are just so many reasons to use LABjs. Why not give it a shot, see if helps your page load speed?
I’m ready! So how do I use LABjs?
To start, simply find your nearest “<script> tag soup” in a page, and replace it with $LAB API calls, like this:
Old and busted:
<script src="framework.js"></script> <script src="plugin.framework.js"></script> <script src="myplugin.framework.js"></script> <script src="init.js"></script>
New hotness:
$LAB
.script("framework.js").wait()
.script("plugin.framework.js")
.script("myplugin.framework.js").wait()
.script("init.js");
In the above example, all scripts load in parallel (by default). “framework.js” needs to execute first, before the others. But “plugin.framework.js” and “myplugin.framework.js” have no dependencies between them, so their execution order is not important (first-come, first-served). “init.js” needs to wait for all 3 scripts to execute before it runs.
The LABjs API has a number of other options and alternate usage patterns, so check out the Documentation and the Test Suite for more information.
Responsibility
I firmly believe it is all our responsibility to do what we can to make the web a better place to live. One of the best ways we can do that is to reduce the amount of time it takes to load friggin’ web pages! So get to it, go download LABjs right away and give your site a makeover in the <script> tag department!


Question/Suggestion: A thought I’ve long had now that Google, Yahoo, and Microsoft all have Ajax CDN’s, is using these CDN’s in parallel and has fail-overs. This is something that would have to be handled by a loader of some sort. My thought is based on the concerns that many people have with using these CDN’s. Primarily, what happens to my site when the CDN goes down? Granted, the chance of Google’s CDN going down is very low, but there is still some additional risk in using a third party to serve critical content (like JS libraries). My thought is to configure a loader to attempt to download a script from additional locations if/when the original reference fails to load (receives an HTTP error) or times out. I think this functionality would be a great benefit to LABjs if one were able to specify a number of fallback locations for a specific script so I could do something along the lines of:
$LAB
.script(“http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js”)
.fallback(“http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js”)
.fallback(“jquery-1.3.2.min.js”)
Thoughts?
@jasonkarns — it’s a good idea, i agree. the problem is in reliably detecting that a script has failed to load. LABjs can’t do so in a script-agnostic way, there’d have to be special test functions tailored to the script that was trying to be loaded.
we’d have to let the user be able to specify some sort of “test” function for a script, which if it fails (with a try/catch), then it assumes the script failed and so it moves on to the next fallback.
i explored the idea in LABjs but came to the conclusion that it was a corner case that would increase the size and complexity of LABjs more than the benefit, at least in terms of having it in the core library that is included on every site, as only some people would feel that was a necessary functionality.
However, I *do* feel like this is a perfect example of something that should be an “add-on” to LABjs. In the same way that I did fLABjs as an adapter add-on for file:/// usage of LABjs (local filesystem loading for development environments), this same type of thing could be layered on top of LABjs actually quite easily.
I have wanted to build core functionality into LABjs that can be used as the nuts and bolts of larger more complex dependency systems and things. This would be a perfect test case for that kind of concept. Basically, you could create a simple wrapper to LABjs that does exactly what you’re suggesting… it might look something like this:
function loadOrFallback(scripts,idx) { if (idx == null) idx = 0; $LAB.script(scripts[i].src).wait(function(){ try { scripts[idx].test(); } catch(err) { if (idx < scripts.length-1) loadOrFallback(scripts,idx+1); } }); } function test_jQuery() { jQuery(""); } loadOrFallback([ {src:"http://some.tld/framework.js",test:test_jQuery}, {src:"http://another.tld/framework.js",test:test_jQuery}, {src:"framework.js",test:test_jQuery} ]);The "loadOrFallback" function tries the first script passed in, and then does a wait() function which runs the "test" function you pass in once that script either loads or fails. if the try/catch of the "test" function throws an error, a recursion back into "loadOrFallback" is done, with the next index of the scripts to load/test. So, this should cascade through the scripts you pass in, trying to load each one, one at a time, and test if it loaded correctly. If not, it should try the next one.
You could do a simple function like that, or you could write a wrapper similar to fLABjs that gives a chaining API compatible with (but augmented) LABjs, and then load both core LABjs and then this other wrapper on top of it.
If I experiment with the full wrapper option and get something tangible, I'll obviously do a blog post about it, so keep an eye out.
Great work, Kyle. The three waterfall charts really highlight the benefits. Newer browsers (IE8, Safari4, FF3.5, and Chrome2) all load scripts in parallel, but as Kyle noted other resources (images, etc.) get blocked. And don’t forget IE6&7 don’t have any parallel script loading. LABjs’ ability to load ALL resources in parallel can greatly accelerate web pages.
Thx, Steve! You certainly deserve a lot of credit for helping get 1.0 so much better at loading in parallel!
Kyle,
I am really impressed by the work you did and the gains in the graphs.
I had an ‘iframe’ parallel loading implementation but it’s not working for the damn IE, so not usable on more than the 60% of browsers around
Your solution is quite appealing since it seems easy to implement on most sites and working cross-browser. My compliments.
[...] sağlamış oluyor. Konuyla ilgili detaylı bir incelemeyi (resimleri da aldığım kaynak olan) http://blog.getify.com/2009/11/labjs-new-hotness-for-script-loading adresinden [...]
I’ve opened the developer tools network tab in both Chrome 16 and Firefox 7 on a Cuzillion test page and both appear to show scripts and images being downloaded -all in parallel.
I’ve read (and you mention FF3.5 here) that newer browsers can download scripts that are referenced in script tags in parallel with each other. But what this seems to show is even newer browsers actually downloading scripts in parallel with other resources like images.
Is LABjs still needed? (or is it still worth using for when a user has an older browser?)
I might just be confused: with http://labjs.com/test_suite/test-script-tags.php Chrome 16 developers tools shows images being downloaded only after the scripts are downloaded. That said, in this case the script tags are in the document’s head whereas with the Cuzillion example they are in the body.
Putting older browsers aside for a moment, is LABjs only relevant if you need to put your scripts in the document’s head?
Thanks for any thoughts
Great question. Yes, script loaders are still relevant even in the latest generation of browsers. And until we ever get rid of
document.write(), they always will be. Browsers have to assume that a script they are loading *might* contain adocument.write(), because if it does, then it could “change everything”™ It could, for instance, write out a<base>tag which completely reinterprets all relative resource paths, meaning the browser may have to throw away any look-ahead work it did in downloading those resources, and re-load them.As such, browsers are forced to, no matter what, hold up the DOM-ready event (that magical moment when a user can interact meaningfully with the page) until it is certain that it in fact DOES know enough about the structure of the page to declare that the “DOM is ready”. This has the effect of blocking DOM-ready until all normally loaded (that is, with
<script>tags) scripts are finished loading and running.DOM-ready is perhaps the most critical moment in the page’s life time when it comes to both performance and perceived-performance (that is, how fast users FEEL your page is). It’s critical to get DOM-ready to happen as soon as possible. No matter where you load your scripts (top or bottom), if you do so with normal
<script>tags, you will ALWAYS block DOM-ready.The common wisdom therefore is to use techniques for loading which unblock the DOM-ready from the script load. How can you do so?
<script async>is one such way. The problem is this only works for one script, or for a set of scripts which are entirely independent, because two or more “async” loaded scripts will not execute in any defined order (they execute ASAP).<script defer>is the next most common suggestion. However, “defer” is *horribly* buggy. Not only does its behavior differ between browsers if it does or does not block DOM-ready, but there are AWFUL bugs (in IE) where if you load two or more scripts with “defer”, and one of them happens to do something like modify the DOM, it can actually cause that first script to *pause* IN THE MIDDLE OF THE CODE EXECUTION, run another script, then come back to the previous code, causing untold numbers of impossible to track down bugs. Try loading jquery.js and jquery-ui.js, both using “defer”, in IE8 and below, and you’ll see what I mean.Anyway, bottom line, in most practical cases, “async” and “defer” are simply not good enough.
Ergo, you need a script loader, which gives you the desired behavior (INCLUDING not blocking DOM-ready) without all the bugs. LABjs is, if nothing else, a polyfill for “defer” (and “async” to an extent), to work the way they “ought to”™ work, but never will (because of legacy).
Thanks for your detailed response. Your post mentioned flash of un-behavioured content and your
noscriptsolution. I’ve experienced this problem (without using a script loader but just by putting script tags just before the closing body tag) and my solution was to place an inline script immediately after the affected element to hide it. noscript sounds much better.If I understand your answer correctly, browers must delay DOM-ready when script tags are used because of the risk that scripts contain
document.write(), but using a script loader we can safely load scripts because we know they won’t contain document.write() (I notice Mozilla says: Never call document.write() from an async script.)Why is DOM-ready a critical moment in perceived performance? I ask because tags in the HTML before the script tags are rendered before those scripts are run, so if your script tags are just before the closing body tag shouldn’t perceived performance be good? (Sorry if that’s rather a noob question -obviously there’s a good answer to that otherwise we wouldn’t need async and defer and script loaders!)