XHR Browser Differences

In my last post, I wrote about an issue with Minefield which drastically slows down local XMLHttpRequests. My supervisor, Dave Humphrey, filed the bug with Mozilla and I had some help from Olli Pettay creating a work-around.

While writing the work-around, I found browsers had a bunch of XHR differences. In this blog, I list some of the things I found.

Property Differences

When I began noticing differences between browsers, I decided to take a step back and first analyze the properties of an XHR instance, so I ran the following code on four browsers (Minefield, Chrome, Opera and WebKit):

var XHR = new XMLHttpRequest();
var str = "";
for(var i in XHR){
  str += i + "\n";

I found Minefield, Chrome and WebKit all return at least the following:


On Minefield, you’ll also get these:


and Chrome and WebKit will give you these additional properties:


Opera is a strange case since it only has these properties:


onreadystatechange Differences

I noted these cases and continued playing around by creating a very simple XHR example which uses the onreadystatechange event:

<span id="debug"></span> 
var debug = document.getElementById('debug');

var XHR = new XMLHttpRequest();
XHR.onreadystatechange = function(){
    case 1: debug.innerHTML += "opened.<br />";break;
    case 2: debug.innerHTML += "got headers.<br />";break;
    case 3: debug.innerHTML += "loading....<br />";break;
    case 4: debug.innerHTML += "done.<br />";break;      

XHR.open("GET", "some_file.txt");

If you run the following code on Minefield, you’ll see something like this:

got headers.

The larger your file, the more likely you’ll see “loading…” printed. It may appear fewer times if the file is small or cached, but will always appear at least once. You probably noticed “opened” was printed twice. This isn’t the case when running the example on Chrome, Opera or WebKit—which only print it once.

onprogress Differences

Now I’ll get to the difference which prompted me to start experimenting with XHR in the first place. Here’s a longer example which uses event listeners:
(Note, I couldn’t run this in Opera since it doesn’t support addEventListener for XHR objects)

<span id="debug"></span>
var debug = document.getElementById('debug');

function progress(evt){
  debug.innerHTML += "'progress' called...<br />";

function abort(evt){
  debug.innerHTML += "'abort' called...<br />";

function error(evt){
  debug.innerHTML += "'error' called<br />";

function load(evt){
  debug.innerHTML += "'load' called<br />";

function loadstart(evt){
  debug.innerHTML += "'loadstart' called<br /> ";

var xhr = new XMLHttpRequest();

xhr.addEventListener("loadstart", loadstart, false);
xhr.addEventListener("progress", progress, false);  
xhr.addEventListener("load", load, false);
xhr.addEventListener("abort", abort, false);
xhr.addEventListener("error", error, false);

xhr.open("GET", "some_file.txt");  

loadstart is called exactly once when the file starts loading. load is called exactly once when the entire file is done being loaded. However, browsers seem to handle the progress event differently. If the file is small enough, on Minefield you’ll see the following output:

'loadstart' called
'load' called

On the other hand, Chrome and WebKit will print out:

'loadstart' called
'progress' called...
'load' called

So Minefield will call the progress event zero or many times while Chrome/WebKit will call it one or many times. Since I was using my work-around on Minefield and the file I was using was small, this gave me some frustration. So I had to add a bit of extra logic to handle this case.

onabort and onerror Differences

I kept experimenting with various methods until I got to onerror. I first tried causing the onerror event to fire by supplying a non-existing file. This only threw an exception. Then I remembered I could stop XHR by pressing escape and wondered if it would cause an onabort or onerror. I created a large file which gave me enough time to interrupt the request, and I found yet more differences.

After pressing escape, I found Minefield calls the onerror event while Chrome and WebKit call the onabort event—which I think is a fairly significant variation.

I’m sure there are more XHR differences between browsers. I’ve only attempted to list a couple here. One solution to this problem involves simply wrapping the XMLHttpRequest object with your own object, which may reduce some headache.


Local XHR Performance Issues with Minefield

Update: The bug mentioned in this blog has been filed here: bugzilla.mozilla.org/show_bug.cgi?id=611320.

Skip the Blog, watch the video:

Or, run the test yourself

When debugging XB PointStream scripts, I usually run the demos locally since the .ASC files tend to be very large, ranging from ~3MB to ~80MB. Trying to XHR that much data remotely takes way too long, but as it turns out, XHR’ing locally can be just as slow.

When using Chromium or WebKit, I can get the Mickey point cloud “downloaded”, parsed and rendered in about 2 seconds. When trying the same thing on Minefield, it can take up to 8 times longer. I doubt there’s anything in our library that’s causing it to be slow, so I can only assume it’s a Minefield issue.

To be completely sure this was an XHR and not a parsing or rendering problem, I created a simple script which tries to XHR a modest 20MB file. I ran the test on Minefield, WebKit and Chromium, recorded it and posted up the video. The video clearly shows Minefield struggling while the other browsers don’t even flinch.

The obvious next step involves filing a bug on bugzilla.mozilla. However, after running a search for “local xhr” I get over 1,000 results. I figure the issue is already known and filed. I’d love to get some help and hear from some Mozilla developers on this one. If not, I’ll have to file the bug and hope it isn’t a duplicate.

Tweening Point Clouds in XB PointStream

Read a boring blog? Screw it, take me to the demo!

Last week Seneca College held its 9th annual Free Software and Open Source Symposium (FSOSS). This year was especially exciting since we had a slew of great WebGL talks and workshops.

Matthew Postill presented his work on SceneCreator, a Web app which enables users to build 3D environment. [Blurb]

Cathy Leung organized an Open Web Game Jam and presented the results [Blurb, Recording]

• I ran a Processing.js Game Development workshop [Blurb, Wiki]

Mickael Medel gave a talk on XB PointStream [Blurb, Recording]

Tips on Tweening

We were lucky to have developers from Arius3D Dan Arnold and Anu Rastogi attend Mike’s talk. Cathy, Mike and I spoke briefly with them after the talk about the project and the presentation. They mentioned we could add a new dimension to future presentations by adding morphing or tweening to the point clouds. Theoretically it should be easy since point clouds lack any sort of topology and thus can be freely deformed.

Tweening in Processing.js

I liked the idea of morphing point clouds, but before implementing it in 3D, I gave it a go in 2D using Processing.js. It turned out to be very simple, only requiring a few lines of code. Check out the demo.

Tweening in XB PointStream

Since the principles were the same, I figured I shouldn’t have issues extending it to 3-space. So I tried morphing two point clouds in XB PointStream, but that didn’t turn out well. Our library just locks up when trying to load more than one point cloud in a single canvas. We have a bug filed for this issue and I think it’s something we should fix.

Since I couldn’t morph two point clouds, I had to cheat and programatically create a second shape to morph into. I chose a sphere since I could just normalize a set of random vectors as the .ASC file streamed. This of course involved cracking open the library and hacking the JavaScript and shaders. But I just wanted to get it done to see if it could work.

The end result (see top) isn’t bad and the use case forces us re-think certain parts of the library. For example, while trying to tween two point clouds I first had to make sure they both contained the same number of points. This took some time. It could have been faster if I could specify to only render every other point. Also, since I had to change the shaders, allowing users to specify their own would be useful.

In the near future we’ll have Mickey morphing into Eggenburg. Now that will be slick!