HTML5 Rocks

HTML5 Rocks

Getting Rid of Synchronous XHRs

By Eric Bidelman at

Heads up! The XMLHttpRequest2 spec was recently changed to prohibit sending a synchronous request when xhr.responseType is set. The idea behind the change is to help mitigate further usage of synchronous xhrs wherever possible.

For example, the following code will now throw an INVALID_ACCESS_ERR in developer channel builds of Chrome and FF:

var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', '/', false); // sync request
xhr.send();

See WebKit Bug, Buzilla Bug

Note: the ability to parse HTML has also been added to XMLHttpRequest but the caveat is that you can't use it unless you're sending an asynchronous request! See HTML in XMLHttpRequest.

Synchronous XHRs are bad for a number of reasons, but MSDN's blog post, "Why You Should Use XMLHttpRequest Asynchronously" has a great explanation of the issues.

This is a generally a great change for the web, but it has the potential to break some existing apps that were relying on synchronous behavior. Please look over your XHR code and update it ASAP to use asynchronous requests.

CSS Filter Effects Landing in WebKit

By Eric Bidelman at

Background

Filter effects have been around for awhile but were designed to work in SVG. They're fantastically powerful at applying effects like color intensity, warping, or blurring to an image before it's composited and rendered into the document.

Well,...way back in 2009 Mozilla said SVG wasn't enough! They ended up taking filters one step further and allowed them on HTML content in Firefox 3.5. Check out Paul Irish's timeless demo of SVG filters on a playing <video>. Again, only works in Firefox but still the bees knees.

Today

Flash forward to the end of 2011 and Adobe (plus others) have been hard at work bringing this amazing technology to CSS. Specifically, I'm referring to CSS Filter Effects 1.0, which WebKit has started to implement.

The magic happens with a new prefixed `filter' property in a style rule:

/* gray all images by 50% and blur by 10px */
img {
  -webkit-filter: grayscale(0.5) blur(10px);
}

Enabling filters directly in CSS means that nearly any DOM element can take advantage of them! Images, <video>, <canvas>,... you name it. You can even see what the web looks like without glasses.

Demo

Try it out! (works in Chrome Canary and WebKit nightlies)

Future

The spec also defines CSS shaders, which will eventually bring OpenGL shader technology to CSS. That's very VERY exciting! However, there are security considerations any time you open up the GPU of a system. For this reason, WebKit only has CSS filter functions implemented for now.

Support

Chrome 18.0.976.0 (currently canary), Webkit nightly

In Webkit nightlies, filters can be applied to hardware accelerated content ( e.g. img { -webkit-transform: translateZ(0); } ). In Chrome, filters on accelerated content are still a work in progress (use the --enable-accelerated-filters flag). This includes <video>, which is accelerated by default.

Transferable Objects: Lightning Fast!

By Eric Bidelman at

Chrome 13 introduced sending ArrayBuffers to/from a Web Worker using an algorithm called structured cloning. This allowed the postMessage() API to accept messages that were not just strings, but complex types like File, Blob, ArrayBuffer, and JSON objects. Structured cloning is also supported in later versions of Firefox.

Faster is better

Structured cloning is great, but it's still a copy operation. The overhead of passing a 32MB ArrayBuffer to a Worker can be hundreds of milliseconds. New versions of Chrome contain a huge performance improvement for message passing, called Transferable Objects.

With transferable objects, data is transferred from one context to another. It is zero-copy, which vastly improves the performance of sending data to a Worker. Think of it as pass-by-reference if you're from the C/C++ world. However, unlike pass-by-reference, the 'version' from the calling context is no longer available once transferred to the new context. For example, when transferring an ArrayBuffer from your main app to Worker, the original ArrayBuffer is cleared and no longer usable. Its contents are (quiet literally) transferred to the Worker context.

To play with transferables, there's a new version of postMessage() in Chrome/V8 that supports transferable objects:

worker.webkitPostMessage(arrayBuffer, [arrayBuffer]);
window.webkitPostMessage(arrayBuffer, targetOrigin, [arrayBuffer]);

For the worker case, the first argument is the ArrayBuffer message. The second argument is a list of items that should be transferred.

Benchmark demo

To see the performance gains of transferrables, I've put together a demo.

The demo sends a 32MB ArrayBuffer to a worker and back using webkitPostMessage(). If your browser doesn't support transferables, the sample falls back to structured cloning. Averaging 5 runs in different browsers, here's what I got:

On a MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo, FF was the fastest using structured cloning. On average, it took 302ms to send the 32MB ArrayBuffer to a worker and post it back to the main thread (RRT - Round Trip Time). Comparing that with transferables, the same test took 6.6ms. That is a huge perf boost!

Having these kinds of speeds allows massive WebGL textures/meshes to be seamlessly passed between a Worker and main app.

Feature detecting

Feature detecting is a bit tricky with this one. My recommendation is to send a small ArrayBuffer to your worker. If the buffer is transferred and not copied, its .byteLength will go to 0:

worker.postMessage = worker.webkitPostMessage || worker.postMessage;

var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
if (ab.byteLength) {
  alert('Transferables are not supported in your browser!');
} else {
  // Transferables are supported.
}

Support: Currently Chrome 17+

Updated (2011-12-13): Code snippet to show webkitPostMessage() signature is different for window and worker.

WebSockets updated to latest version in Chrome Canary

By Eric Bidelman at

The WebSocket API has been rev'd to the latest version (13) in Chrome Canary. The developer-facing changes are very small, but are incompatible with the older version.

Here's the scoop:

  • Change the origin header name: Sec-WebSocket-Origin -> Origin
  • Sec-WebSocket-Version header value: 8 -> 13

Workers ♥ ArrayBuffer

By Eric Bidelman at

As of crbug.com/73313, Chrome 13 and FF5 support sending an ArrayBuffer (or Typed Array) to/from a Web Worker. For example:

worker.js

self.onmessage = function(e) {
  var uInt8Array = e.data;
  postMessage("Inside worker.js: uInt8Array.toString() = " + uInt8Array.toString());
  postMessage("Inside worker.js: uInt8Array.byteLength = " + uInt8Array.byteLength);
};

main.html

var uInt8Array = new Uint8Array(new ArrayBuffer(10));
for (var i = 0; i < uInt8Array.length; ++i) {
  uInt8Array[i] = i * 2; // [0, 2, 4, 6, 8,...]
}

console.log('uInt8Array.toString() = ' + uInt8Array.toString());
console.log('uInt8Array.byteLength = ' + uInt8Array.byteLength);

worker.postMessage(uInt8Array);

Why is this exciting?...binary data!

Instead of the browser serializing your postMessage() data to a JSON object, it uses the structured clone algorithm to copy the ArrayBuffer to the worker's context, and vice versa. This opens up a real potential for workers that we haven't seen before. That is, being able to easily pass binary data between main app and worker thread.

Typed array I/O makes intense image manipulation, sound processing, and heavy webgl calculations much more feasible. For example, one could read a File as an array buffer or fetch a Blob using XHR2 and pass the result directly to a worker. No more base64 encoding the data :)

In my opinion this is one of those nitpicks workers should have included from the start. It just makes sense.

What's different in the new WebSocket protocol

By Eiji Kitamura at

The WebSocket protocol specification has recently been updated to solve previous security concerns and is largely stable. Below is a summary of the changes involved, along with some notes on current implementations.

What has been changed since WebSocket HyBi 00?

  • The protocol frame format has been changed. HyBi 00 used to use "0x00" for head and "0xff" for tail for each frame. HyBi 10 now uses new format like following:

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/63)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+
  • Security issues have been addressed.
    • Sec-WebSocket-Key and Sec-WebSocket-Accept are added in place of HyBi 00’s three keys. The browser gives randomly generated number to Sec-WebSocket-Key. Then, the server uses it with WebSocket protocol’s specific GUID (258EAFA5-E914-47DA-95CA-C5AB0DC85B11) and SHA1 / BASE64, etc to return Sec-WebSocket-Accept so that browser can confirm that it understands WebSocket. This prevents a cross-protocol attack.
    • On each frame, frame masking is now required. This prevents cache poisoning on proxy. Sec-WebSocket-Origin is added to prevent access from scripts that the service provider isn’t aware of.
    • Sec-WebSocket-Origin is added in place of HyBi 00’s Origin key to prevent access from scripts that the service provider doesn’t aware of. Note that this will be just “Origin“ on HyBi 11.
  • JS API changes
    • subprotocol can now be array, allowing a method signature of new WebSocket(String url, Array subprotocol)
    • .protocol attribute [String]
    • .binaryType attribute [Blob|ArrayBuffer]
    • .extension [String]
    • Status code and reason (why the connection is closed) have been added to CloseEvent. The close() function has also been changed to accept these two arguments accordingly.
  • Extensions
    • Sec-WebSocket-Extensions is added. Proposed extensions are:
      • deflate-frame makes frames compressed at source and extracted at destination.
      • x-google-mux to support multiplexing but is in early stage.

Is there compatibility between HyBi 00 and HyBi 10 on both server and browser implementation?

  • Server implementations can support both HyBi 00 and HyBi 10 by looking at the handshake HTTP header. However, it is not recommended to support HyBi 00 since it’s known to be vulnerable.
  • The WebSocket JavaScript API is largely similar between old and new versions. But as noted above, we don’t recommend supporting HyBi 00 since it’s known to be vulnerable.

Which browser support HyBi 10?

  • Chrome 14 supports HyBi 10 protocol although the WebSocket JavaScript API changes mentioned above are still on the way. Firefox 7 is also planned to support HyBi 10.

insertAdjacentHTML Everywhere

By Ernest Delgado at

If we want to insert content in a HTML document we have three ways to do it:

  • Using DOM methods like createNode and appendChild
  • Using Document Fragments
  • Using innerHTML

One can arguably say we also have document.write for few use cases.

innerHTML has been standarized in HTML5 and with it a brother method insertAdjacentHTML which works as innerHTML but allows us to define more specifically where we want to insert the HTML content: beforeBegin, afterBegin, beforeEnd and afterEnd.

var ul = document.getElementById("list");
ul.insertAdjacentHTML("beforeEnd", "<li>A new li on the list.</li>");

Back in 2008 John Resig wrote an article about insertAdjacentHTML with this conclusion:

Having browsers implement this method will dramatically reduce the amount of code needed to write a respectable JavaScript library. I'm looking forward to the day in which this method is more-widely available (along with querySelectorAll) so that we can really buckle down and do some serious code simplification.

Until now, the main issue with insertAdjacentHTML has been its lack of browser support. With Firefox implementing insertAdjacentHTML as of version 8, it will available in all major browsers including mobile browsers. If you want to use it now and make sure it works in Firefox versions earlier than 8 you can use this polyfill.

Announcing New Game, the conference for HTML5 game developers

By Seth Ladd at

Please join us at New Game , to be held November 1-2, 2011 in San Francisco, CA. New Game is the first HTML5 game conference for North America.

Our theme is “HTML5 games are here today” and the conference will feature speakers building and releasing production HTML5 games for desktop, laptop, and mobile.

Darius Kazemi from Bocoup is the conference director, and Google is the premier sponsor of the event.

A Call for Participation is now open, if you are building real HTML5 games today, we want to help you tell your story. Potential topics can include multi-player HTML5 games over Websockets, HTML5 audio and the Web Audio API, debriefs of production releases of HTML5 games, WebGL, asset pipelines, and business models.

See you at New Game this fall!

Using Cross-domain images in WebGL

By Ernest Delgado at

WebGL specification has an important update on how to request images, cross-domain. The feature has already been implemented in Chrome 13 and is coming soon to Firefox 5.

Just use image.crossOrigin method on the client side and if you can edit the server just add support to it.

Read all the details in Using Cross-domain images in WebGL and Chrome 13.

Welcome to updates.html5rocks.com!

By Eric Bidelman at

The open web platform is changing at an ever increasing rate. Many developers find it hard to keep up with the pace the browsers are implementing new HTML5 features, including ourselves. There are a ton of new APIs, demos, libraries, and announcements, every day.

In an effort to info developers quicker, we've built this "HTML5 Update Stream" to highlight the cool things worth sharing. At first, the updates will include new tutorials on HTML5Rocks, demos we find, and Chromium updates. Over time, we hope to automagically aggregate more stuff.

Posts in the stream will contain more content than a Tweet but less than a blog post. Look out for useful code snippets, screenshots, demo links, and more!

Cheers,

Eric