+$A few weeks ago, I presented "+$The Web Can Do That!?+$" at Google I/O 2012:

+$Unlike some of the other bleeding edge HTML5 talks I’ve given in the past, this year’s focus was to demonstrate practical uses of the latest web tech and show it in action. After all, flashy demos are the bees knees. No one believes a bunch of bulleted lists and hand waving!

+$In this article, I’m going to dive into the top five most amazing things from “The Web Can Do That!?” If you’re antsy, the source for everything is up on myGitHub上+$and the presentation itself can be found at+$htmlfivecan.com

+$Heads up: Some of this stuff is still very new. Your best bet is to try the slide deck and demos in Chrome 21+.

+$1. CSS for web apps

+$CSS has brought us to some magical places, but unfortunately many of its layout and presentation capabilities (I’m looking at you, absolute positioning and floats) fail miserably in the age of modern web development. Problem is, we’re not building websites any more – we’re building apps. The requirements are very different. Many of CSS’s original constructs do a poor job in situations like responsive design.

+$Lucky for us the CSS Working Group is three steps ahead of us! It's proposed a number of new specs to directly address app layout and design issues. CSS网格布局+$Hierarchies地区,和+$Flexible Box Layout Module仅举几例。

+$Let’s cover one of these: CSSFlexbox的


+$I’m particularly stoked aboutFlexbox的+$because it enables me to centre content both horizontally and vertically with three lines of CSS. No more nasty top/left 50% and negative margin tricks.

+$Here's how it works:

+$.box { display: +flex; +justify-content: center; +align-items: center; }

+$Note: I’m using “+” through this article to indicate a vendor prefix (eg “+flex” should be -webkit-flex, -ms-flex, etc).

+$This example produces the following layout:

+$Try It!

显示:flex+$tells a parent container to become a 'flex container'. In the illustration above, Mr. Red

+$is our flex container and its content (the three blue children) are 'flex items'. To pack A, B, and C to the centre of the main axis and cross axis, we+$justify-content: CenterAlign-items:center, 分别。

+$Ordering And Orientation

+$It doesn’t stop there. Another fantastic property of Flexbox is that we can fully separate the order of our markup from the way it’s presented. This is achieved through two new CSS properties,订购弯曲方向+$. Order controls the sequence in which sibling items appear.+$Flex-direction+$modifies their orientation (row vs column).

+$Let’s say that instead of a row, we wanted to arrange A, B, and C into a column ... no problem!Flex-direction:列+$arranges the child along the cross axis instead of the main axis. Note, by default items render in the order they’re defined in the markup, but we can easily override that. By giving B a lower order value than its siblings, it will precede the others children.

+$.box { +flex-direction: column;}.box > :nth-child(2) { +order: -1;}


+$Try It!

+$Keep in mind, we didn’t change the source at all. It’s still A, B, then C. Flexbox gives us the ability to arrange content independent of how our source is defined.


+$The bread and butter of Flexbox is its 'flexibility' feature. In addition to alignment, orientation, and ordering, one can also instruct items to grow/shrink to fill the available space around them. This is done through the柔性属性。

柔性+$property takes three values. The first is a positive flex value: how much the element can grow compared to its siblings. The second is a the negative flex: how much it’s allowed to shrink. The third value is the desired width of the item.

+$Modifying our previous example, we can use the柔性+$property To Make+$three times bigger than its buddies:

+$.box > * { +flex: 1 0 auto;}.box > :nth-child(2) { +flex: 3 0 auto;}

+$The result is exactly what you'd expect:

+$Try It!


+$Requires: Chrome 21

演示+$illustrates how easy it is to create that 'Holy Grail' layout (header, 3 column, footer) with CSS Flexbox. Best of all, the entire app is responsive. Try resizing your browser window.


+$The new Flexbox spec is implemented in Chrome 21 and IE 10. I’m told Mozilla is+$actively Working+$on an implementation as well.

+$2. One-way Data Binding

+$Databinding is something every modern web app needs. Until Web Components’ MDV is a ready, we’ll have to rely on JavaScript frameworks like要么+$to fill in the gaps for us. Or do we?

+$Angular is one of my favourite MVC frameworks at the moment because it’s simple. I don’t need to learn new APIs or templating syntax to use it. It relies on raw HTML as its templating language and pure JS for controller logic. That, I like!

+$One-way data binding in Angular is a cinch with a little markup:

Volume: {{val}}/100

+$As the user moves the slider, the model named+$val+$is updated and its corresponding template variable is automagically recalculated. Angular does the heavy lifting for us by setting up hidden event listeners and re-rendering our view as the model changes.

+$Using HTML5 data-* attributes

+$The very same thing can be done using HTML5 features and+$a Clever Trick+$:before/:after+$pseudo elements. However, for the HTML5 case we don’t have the luxury of automatic binding. The process looks like this:

  • 模型: 数据-*+$attribute. Use CSSATTR()+$to get the value.
  • 视图:+$render generated content to+$:before/:after+$pseudo Elements.
  • +$How To Bind?:+$Hook up an event listener to watch for changes in the data model.

+$Implementing this idea, we get the exact same slider as the Angular example, but sans framework!


+$Try It!


+$Perhaps a better (and more semantic) way to do one-way databinding in HTML5 is to use the new+$+$element. Opera and FF have had+$+$for ages, but it’s only recently landed in WebKit.

+$The binding process for this one looks like:

  • 模型: +$+$values specified in the+$元件。
  • 视图:+$a Regular Joe元件。
  • +$How To Bind?+$: reference the id of the data list with the名单属性。

+$The code is equally simple:


+$Try It!

+$+$is a great way to specify pre-defined list options for an+$element. Think an autocomplete form populated from anIndexedDB的+$database.


+$Believe it or not, all of these framework-free data binding techniques are supported in all modern browsers!

+$3. Access A Filesystem

+$Most decent apps need file I/O at some point in their lifecycle. The+$HTML5 Filesystem API+$brings a proper filesystem to the web. No plug-ins, no fuss. With it, users can persist data to files and folders to a filesystem sandboxed to your web app’s origin.

+$To open the filesystem, call+$window.requestFilesystem+$(vendor prefixed of course):

+$window.webkitRequestFileSystem( TEMPORARY, // Storage type: PERSISTENT or TEMPORARY 1024 * 1024, // size (bytes) of needed space initFs, // success callback opt_errorHandler // opt. error callback, denial of access);

+$To be clear, we’re not reading/writing data to the user’s My Documents or My Pictures folders on the native OS. The HTML5 Filesystem can only interact with the sandboxed filesystem that’s created for your app. This also means you cannot modify data in another web app’s filesystem.

+$A common use case for this API is an AppCache replacement. As an example, dynamically caching an image file is a breeze:

+$var xhr = new XMLHttpRequest();xhr.open('GET', '/path/to/image.png', true);xhr.responseType = 'arraybuffer'; // We want a byte array, not a string.xhr.onload = function(e) { window.requestFileSystem(TEMPORARY, 1024 * 1024, function(fs) { // fs.root is the root DirectoryEntry for the filesystem. fs.root.getFile('image.png', {create: true}, function(fileEntry) { fileEntry.createWriter(function(writer) { writer.onwriteend = function(e) { ... }; writer.onerror = function(e) { ... }; writer.write(new Blob([xhr.response], {type: 'image/png'})); }, onError); }, onError); }, onError);};xhr.send();


+$Requires: Chrome

+$Filesystem Playground+$is a visual UI on top of the HTML5 Filesystem API. The entire app is client-side. Some of the functionality includes dragging in files and folders from the desktop to import them into the web app, create empty folders, preview files, rename items, download them and so on.

+$HTML5 Terminal+$emulates an old-school command line. It too sits on top of the Filesystem API.

+$After you’ve created a few files, I dare you to try the+$3d+$command!


+$THe HTML5 Filesystem API is Chrome-only at the moment. It also+$appears+$Mozilla is steadfast on keepingIndexedDB的+$as the sole solution for File I/O. In my experience,IndexedDB的+$is far too difficult to use for stashing files and creating any kind of a folder hierarchy. It’s an abuse of the API. In the interim, I’ve created+$idb.filesystem.js+$, which polyfills the Filesystem API into browsers that only supportIndexedDB的+$. That means any browser that supports IDB should also (theoretically) support the Filesystem API. You’re welcome!

+$If you want to take a deeper dive into the Filesystem API, check out my book,+$Using the HTML5 Filesystem API+$. In Addition, Give+$filer.js+$a look. It’s a handy wrapper library that abstracts the API into UNIX calls such as (cp, mv, mkdir).

+$4. Access Native Hardware

+$You’re probably saying, “Whaaaat!? The web can’t access native hardware”. For the most part I have to agree. Sadly, accessing things like USB, Bluetooth, and UDP are just not possible on today’s web. We’ve seen frameworks such as PhoneGap pave the way here – but the fact remains, the drive-by web doesn’t have all of the APIs developers are foaming at the mouth for.

+$Device access is hot topic these days; so much so that the W3C formed the设备API工作组+$in August 2011 to tackle the issue head-on. Recent projects such as+$Chrome AppsFirefox OS+$have also started to explore such endeavours.

+$What can we do today?

+$The ability for web apps to leverage high level JS APIs that sit on top of underlying hardware isn’t foreign web. The last few years have brought us a bunch of this kind of stuff:

  • +$Geolocation API (positional GPS)
  • +$Device Orientation API (accelerometer)
  • +$WebGL (GPU)
  • +$HTML5 Filesystem API (sandboxed file I/O)
  • +$navigator.onLine/+$navigator.connection+$(network Connectivity)
  • +$Battery API
  • +$Gamepad API (USB access to a specific device)
  • +$WebRTC (voice/video processing) / Web Audio API (core system audio)

+$...the list goes on. Let’s focus on the last one.

+$Microphone Access

+$Since the dawn of mankind, one of the coveted asks of the web has been proper camera and microphone access (without a plug-in). A first step was the+$x-webkit-speech+$property implemented in Chrome:


+$This attribute was exciting. With one attribute, we got minimal access to the user’s microphone and a totally new way for users to interact with our app.

+$Aside: It’s worth noting browsers are expanding on this feature with a more robust+$Speech JavaScript API+$in the coming months.

+$Today we can do better thanks to the recent work in the WebRTC space. At the core of WebRTC isGetUserMedia+$, an API by which enables an app to request access to the mic/camera:

+$window.URL = window.URL || window.webkitURL;navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;navigator.getUserMedia({audio: true, video: true}, function(stream) { document.querySelector('video').src = window.URL.createObjectURL(stream);}, function(e) { console.log(e);});

+$What I really like aboutGetUserMedia+$is that it’s reusing older parts of the platform, namely HTML5+$. Instead of setting the+$video.src+$to a movie file, we set it to a blob URL created from the camera stream. A live feed. This kind of integration is a great example of older APIs co-existing with newer ones.

+$Recording Video

+$One thing I’m looking forward to (+$which is not quite ready yet+$) is being able to record a+$LocalMediaStream+$as It Happens:

+$var localMediaStream;var recorder;function record(button) { recorder = localMediaStream.record();}function stop(button) { localMediaStream.stop(); recorder.getRecordedData(function(blob) { // Upload blob using XHR2. });}


+$Basic Support ForGetUserMedia+$is in Chrome 20 (enabled in about:flags), Opera 12, and Firefox 17 (currently nightly). The flag is going away in Chrome 21.

+$5. Stream Multimedia

+$Something I hear a lot from web developers is: “Can HTML5 stream audio?” Turns out, it absolutely can. The secret sauce is a blending of binary WebSockets and the Web Audio API.

+$For many moons, one could only send string data through a WebSocket. That limitation alone prevented some crazy-cool apps from being built using WebSockets. We have JS Typed Arrays and binary data in the web platform now. Why shouldn’t I be able to stream a file? The spec authors and browser vendors eventually caught onto this little idea and now, implementations include a newer+$send()+$method that supports sending binary data.

+$The concept is similar to XHR2. You simply set the format of the data you’re exchanging. For example, to send a blob/file, set the+$.binaryType财产到+$blob

+$var socket = new WebSocket('ws://example.com/sock', ['dumby-protocol']);socket.binaryType = 'blob'; // or 'arraybuffer'socket.onopen = function(e) { window.setInterval(function() { // Send off data when nothing is buffered. if (socket.bufferedAmount == 0) { socket.send(new Blob([blob1, blob2])); // presumably image data. } }, 50); // rate limit us.};socket.onmessage = function(e) { document.querySelector('img').src = window.URL.createObjectURL(e.data);};

+$On the receiving end (eg the+$onmessage+$handler), we can use the image file (+$e.data+$) directly by creating a blob URL from it. No more needing to Base64 encode/decode data on either end! Performance boosts ... me likey!

+$Streaming Audio

+$Binary websockets allow for some pretty interesting use cases, including streaming audio.

+$Instead of covering the technique in great detail, you can check out my+$audio_streamer+$demo to see how the code works. However, the process is straightforward:

  1. +$On the DJ machine: a) Use the Web Audio API to+$decodeAudioData()+$on an entire .mp3 file. b) Once the file is decoded, slice the entire+$AudioBuffer+$into smaller chunks. We don’t want to send the whole thing at once. c) Use a simple NodeJS server to send each audio chunk (as an+$ArrayBuffer+$) over a binary WebSocket.
  2. +$On the listener’s machine: a) Use the Web Audio API to load + schedule each audio chunk at the precise time when it’s supposed to play. This seamlessly 'recreates' the audio for the listener as if it were a single file.

+$The result of this workflow is essentially streaming audio ... something that wouldn’t be possible without two new features in the web platform: binary WebSockets and the Web Audio API.

+$WebRTC Data Channels

+$Perhaps the future of file sharing is the+$DataChannel API+$from the WebRTC effort. Unfortunately, it’s still being implemented in Chrome and FF. That API aims to enable true peer-to-peer data exchange in a real time fashion.


+$Chrome, FF, IE 10, and Safari support binary WebSockets. Chrome and Safari are the only browsers supporting the Web Audio API.


+$It pains me that the whole 'native vs web' debate is still a thing. I’m a web developer. I couldn't care less about what native can do but I care everything about what the web can do! Sure, there are holes in our platform, but we’re addressing those gaps with an ever+$ever-increasing+$number of APIs. Things such asWeb组件+$are going to change the way we build web apps. In this spirit, I’d like to see us web folk stop squaring off with the other guys and shift the conversation more around what the web platform can do. A lot of people don’t even realise what’s possible.

+$Hopefully This Article,+$htmlfivecan.com,和+$htmlfivewow.com+$have proven the web can often do more than meets the eye.