HTML5 Rocks

HTML5 Rocks

CSS Background shorthand coming to mobile WebKit browsers

By Pete LePage at

Earlier this year, WebKit updated the behavior of the CSS background shorthand property. With this change, the background shorthand property will reset the background-size to its default value of auto auto if it’s not set in the shorthand declaration. This change brings Chrome and other WebKit browsers in compliance with the specification and matches the behavior of Firefox, Opera and Internet Explorer.

With Chrome for Android moving forward to current revisions on WebKit, this change is now rolling out in Android. Because this was a fix to webkit, sites that tested in multiple browsers are likely not affected.

Old way of doing things

// background-size is reset to auto auto by the background shorthand 
.foo {  
  background-size: 50px 40px;
  background: url(foo.png) no-repeat;
}

The background shorthand property does not contain any size property and will therefore reset the background-size to its default value of auto auto.

Correct way to specifying image size

// background-size is specified after background
.fooA { background: url(foo.png) no-repeat; background-size: 50px 40px;
}

// background-size is specified inline after position
.fooB { background: url(foo.png) 0% / 50px 40px no-repeat;
}

In fooB, specifying a background-size in the background shorthand requires the position (0%) be specified first, followed by a forward slash then the background-size (50px 40px). |

Fixes for existing code

There are several options that will provide an easy fix for this:

  • Use background-image instead of the background shorthand property.
  • Set the background-size last, with a higher specificity than any other CSS rules that set background on that element, and don’t forget to check and :hover rules.
  • Apply the !important property to background-size.
  • Move the sizing information to the background shorthand property.

Bonus: background image offsets

In addition to this change, there’s now greater flexibility in positioning the image within the background. In the past, you could only specify the image position relative from the top left corner, now you can specify an offset from the edges of the container. For example setting background-position: right 5px bottom 5px; the image will be positioned 5px from the right edge and 5px from the bottom. Check out this sample on JSBin

Canvas-driven background images

By Eric Bidelman at

Programmatically animating background images

There are two primary ways people animate background images:

  1. Use CSS sprites to update a background-position in JS .
  2. Hacks with .toDataURL() .

The first works great if you have the image ahead of time, but what if your source needs to be programmatically generated, say, by a <canvas>? The solution to #1 is to use .toDataURL() on the canvas and set the background to the generated URL:

while(1) {
  var url = canvas.toDataURL('image/jpeg');
    el.style.background = 'url(' + url + ')';
 }

There are two problems with this:

  1. data: URLs add a ~33% size overhead to the resulting image.
  2. A ton of DOM touching (el.style).

Both of these methods are inefficient: unacceptable for an always-silky-smooth 60fps web app.

Using 2d canvas as a background

DEMO

Turns out, there's a non-standard API which WebKit has had for years that can take canvas as the source for a background. Sadly however, there's no published spec for this feature.

First, instead of specifying a URL for the back:

.bg {
  background: url(bg.png) no-repeat 50% 50%;
}

use -webkit-canvas(), referencing a string identifier to a canvas context:

.canvas-bg {
  background: -webkit-canvas(animation) no-repeat 50% 50%;
}

Next, we need to create the 2d context with a special version of .getContext():

var ctx = document.getCSSCanvasContext('2d', 'animation', 300, 300);

Note: this method is on the document object and not the canvas. The second argument is the same name used in -webkit-canvas().

Further information from Dave Hyatt:

There is only one CSS canvas for a given global identifier per document. When you obtain the drawing context you also specify the size. The canvas will not be cleared as long as you repeatedly request the same size. Specifying a new size is equivalent to the kind of behavior you’d expect if you resized a <canvas> element, and so the canvas buffer will be cleared.

All objects that observe a CSS canvas of the same name are sharing that canvas. This means that (similar to how animated GIFs work), you can do animations and have them happen in lockstep on all the clients of the canvas. Drawing changes will be propagated to all clients automatically.

Animations

As seen in the demo, we can reuse requestAnimationFrame() to drive an animation. This is great, because once things are hooked up, the association between CSS and the canvas element is preserved. There's no need to fiddle with the DOM.

Demo not animated in Chrome?

The current stable channel of Chrome (version 23) has crbug.com/161699, which prevents a requestAnimationFrame() animation from updating the background properly. This has been fixed in Chrome 25 (currently Canary). The demo also should work well in the current Safari.

Performance benefits

We're talking canvas here. Hardware accelerated animations are now fully in play (at least for the browsers this feature works in). And just to reiterate, there's no need to molest the DOM from JS.

Using webgl as a background

Hold on a sec. Does this mean we can power a CSS background using webgl? Of course it does! WebGL is merely a 3d context for canvas. Just swap in "experimental-webgl" instead of "2d", and voila.

var gl = document.getCSSCanvasContext('experimental-webgl', 'animation', 300, 150);

Here's a proof of concept that contains a div with it's background drawn using vertext and fragment shaders: DEMO

Other approaches

It's worth noting that Mozilla has had -moz-element() (MDN) for quite some time. This is part of the CSS Image Values and Replaced Content Module Level 4 spec and allows you to create an image generated from arbitrary HTML: videos, canvas, DOM content,...you name it. However, there are security concerns with having full access to snapshot images of the DOM. This is primarily why other browsers have not adopted said feature.

Interactive Globe with CSS shaders & Google Maps

By Paul Irish at

Recently, I have read news on Webmonkey that Adobe’s CSS Shaders proposal, which will bring high-quality cinematic effects to the web through some new CSS tools, has been accepted by the W3C. If you haven't seen it yet, watch the video below:

Google Chrome's latest Canary added support for CSS shaders, so I decided to experiment with them.

In this experiment, I used custom vertex shader (spherify.vs) and fragment shader (spherify.fs) to create a globe with Google Maps.

<iframe
  class="globe"
  src="http://maps.google.com/?ie=UTF8&amp;ll=14.597042,-15.625&amp;spn=158.47027,316.054688&amp;t=h&amp;z=2&amp;output=embed"
  scrolling="no"></iframe>
.globe {
  width: 550px;
  height: 550px;
  border: 0;
  -webkit-filter: contrast(1.4) custom(url(shaders/spherify.vs) mix(url(shaders/spherify.fs) multiply source-atop),
    50 50 border-box,
    amount 1,
    sphereRadius 0.5,
    sphereAxis -0.41 1 0.19,
    sphereRotation 43.5,
    ambientLight 0.15,
    lightPosition 1 0.87 0.25,
    lightColor 1 1 1 1,
    transform perspective(500));
}

Here, we're applying a vertex shader (spherify.vs) which will operate on a mesh that has 50 lines and 50 columns (50 50 border-box). Feel free to read the source of the vertex shader: spherify.vs. It's written in GLSL but you can probably follow along.

The mix() function provides basic functionalities for color manipulation like blending and alpha compositing on a fragment shader.

We can change the shere's radius, axis, rotation right in the CSS. In this example we set the value of the sphereRadius: 0.5 and it gives original sphere size.

Enjoy the demo!

Below is a video of the effect. If you've got shaders enabled you can play with the real thing right below!

If you just see a flat google maps above, you can enable it with the instructions below

Browsers support: CSS shaders

This is currently cutting-edge, so it's only available in the latest Google Chrome Canary and WebKit nightly. To enjoy the full experience you'll need to turn a few knobs.

Chrome Canary steps:

  • Type about:flags in the browser's navigation bar
  • Find "Enable CSS Shaders". Enable it
  • Relaunch the browser

WebKit nightly steps

  • Download and install WebKit nightly for Mac OSX
  • Open the browser's preferences panel. Go to Advanced tab and tick to show Develop > Enable WebGL menu in the menu bar.
  • In the browser's menu bar select Develop

How to measure browser graphics performance

By Ilmari Heikkinen at

Benchmarking browser graphics in a nutshell: draw as much as you can while maintaining a smooth frame rate. Once your framerate drops, you know how much you can draw per frame. End of post. ...No? Ok, I’ll explain some more.

Example time! Here’s a small code snippet with a benchmarking tick function. The tick function calls a draw function with an increasing draw load until the draw takes consistently longer than 33 ms.

var t, previousTime;
var drawLoad = 1;
var slowCount = 0;
var maxSlow = 10;
t = previousTime = performance.webkitNow();
var tick = function() {
    var maximumFrameTime = 1000/30; // 30 FPS
    t = performance.webkitNow();
    var elapsed = t - previousTime;
    previousTime = t;
    if (elapsed < maximumFrameTime || slowCount < maxSlow) {
        if (elapsed < maximumFrameTime) {
            drawLoad+=10;
        } else {
            slowCount++;
        }
        draw(drawLoad);
        webkitRequestAnimationFrame(tick);
    } else {
      // found maximum sustainable load at 30 FPS
      document.getElementById('res').innerHTML = ("could draw "+(drawLoad)+" in " + 
          maximumFrameTime + " ms");
    }
};
webkitRequestAnimationFrame(tick);


See the live example at jsFiddle

You can see how the benchmark keeps drawing more and more until it hits the point where it slows down. This is a nice and simple way to figure out how much you can draw at a smooth frame rate. You can also plug in your own draw function to the example and do some custom benchmarking, yay!

Common caveats and pitfalls when benchmarking browser graphics

So, if the above example is the nice way to do it, what are the not so nice ways? The ways that lead to you benchmarking unrelated things or that give you weird performance metrics that don’t seem to have anything to do with how fast your app is running. Glad you asked, here are the two most common ones I’ve seen around the web.

Measuring max FPS: draw a little bit every frame and measure the FPS. It doesn’t work well for measuring graphics performance on Chrome because the underlying graphics implementation is synchronized to the screen refresh rate (so you get max 60 screen updates per second). Measuring draw call speed isn’t going to be very helpful either as Chrome’s drawing system puts your drawing commands into a command buffer that gets executed on the next screen refresh.

Using setTimeout for measuring graphics performance is another bad idea. The setTimeout interval is capped to 4 ms in browsers, so the most you can get out of it is 250 FPS. Historically, browsers had different minimum intervals, so you might have had a very broken trivial draw benchmark that showed browser A running at 250 FPS (4 ms min interval) and browser B running at 100 FPS (10 ms min interval). Clearly A is faster! Not! It could well be that B ran the draw code faster than A, say A took 3 ms and B took 1 ms. Doesn’t affect the FPS, as the draw time is less than the minimum setTimeout interval. And if the browser renders asynchronously, all bets are off. Don’t use setTimeout unless you know what you’re doing.

How to do it then

A better way to benchmark is to use a realistic drawing load and multiply it until the frame rate starts to chug. For example, if you’re writing a top-down game with a tilemap terrain, try drawing the tilemap every frame and seeing if it runs at 60 FPS. If yes, increase load (draw tilemap twice every frame, with a clear in between). Continue increasing until the FPS drops to a new stable level. Now you know how many layers of tilemap you can draw per frame.

Different graphics applications have different needs, so you should write your benchmarks with that in mind. Measure the graphics features that you are using in your app. When you find a slow scenario, try to reduce it down to the smallest piece of code that reproduces it (and file a bug report at new.crbug.com if it should be faster.)

To see how to write high-performance web graphics code, check out this Google I/O 2012 talk by Nat Duca and Tom Wiltzius from the Chrome GPU team:

Taking advantage of GPU acceleration in the 2D canvas

By Ilmari Heikkinen at

The 2D canvas in Chrome got some hardware acceleration love back in February. The acceleration makes drawing 2D sprites really fast, as the implementation is using the GPU to do drawImage.

You can check out this example for the kinds of things the acceleration enables. The demo is drawing 180 transformed 256×256 PNG sprites on a full-window canvas and running an N-body simulation on them to make the sprites gravitate towards each other. And it all runs smoothly at 30 to 60 frames per second on a low-powered laptop.

There are still some rough edges in the acceleration implementation, so you may see performance degradation in some use cases (please let the Chrome team know through new.crbug.com so that we can fix them!)

SwiftShader brings software 3D rendering to Chrome

By Ilmari Heikkinen at

SwiftShader is a software 3D renderer used in Chrome 18 that lets you use CSS 3D and WebGL even on blacklisted GPUs. SwiftShader is available only on Windows and kicks in when you visit a site that uses 3D graphics features.

The first time you run Chrome, it checks if your GPU is blacklisted. In the unfortunate case that it is, Chrome downloads and installs the SwiftShader component in the background. After the component is installed, you can view 3D content. If you visit a 3D site before the component has finished installing, you may need to close and re-open the tab to view the site.

The performance of SwiftShader should be good enough to view simple 3D content.

To force-enable SwiftShader for testing purposes, start Chrome from the command line with the —blacklist-accelerated-compositing and —blacklist-webgl flags.

You can read more about Chrome 18’s new graphics features (including GPU-accelerated 2D canvas) at the Chromium Blog

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.

Use mediump precision in WebGL when possible

By Ilmari Heikkinen at

Heads-up from our friends at Opera, who have been testing WebGL on actual OpenGL ES 2.0 hardware: many demos and applications use highp precision in fragment shaders when it’s not really warranted.

Highp in fragment shaders is an optional part of the OpenGL ES 2.0 spec, so not all hardware supports it (and even when they do, there may be a performance hit). Using mediump will usually be good enough and it will ensure that your applications will work on mobile devices as well.

In practice, if your fragment shader previously started with

precision highp float;

Changing it to the following should do the trick:
precision mediump float; // or lowp

WebGL demo roundup

By Ilmari Heikkinen at

Developers keep pushing the boundaries of what's possible to do in the browser. Here are some awesome new WebGL demos from around the web to showcase what your browser can really do. First off, have a gander at this impressive three.js dynamic terrain rendering demo from AlteredQualia. The second demo is Nouvelle Vague from the French web design agency Ultranoir, and it shows a very particular way to read tweets. Mr.doob comes back with another three.js demo, this time using data captured by a Kinect. Also using three.js is One Millionth Tower, a documentary funded by the Canadian National Film Board. And last, these guys got WebGL running on an iPad 2 through a WebView hack.

In other news, the Google Data Arts Team launched a workshop page to share the technology they've used to build some amazing demos. The page has a great tutorial on using dat.GUI, go check it out!