art with code

2011-09-27

Basics of Three.js Presentation


The slides for my Basics of Three.js talk are now up at fhtr.org/BasicsOfThreeJS

They take you through getting started with Three.js, building small apps and using custom shaders. There's a lot of live examples to play with, including the world's worst 3D modeler (pictured). You can fork the deck at github.com/kig/BasicsOfThreeJS

If you want to show off your awesome Three.js skills, add a new section to the deck and send me a pull request. Remember to add your name to the credits if you do!

2011-08-29

Canvas image filter library

Here's a small image filter library for the canvas element: github.com/kig/canvasfilters

It's based on the HTML5Rocks Canvas Image Filters article.

2011-08-27

WebGL Filesystem Visualizer


I put the HTML5WOW 3D filesystem visualizer demo online at fhtr.org/wfsv. Works only on Chrome as it uses the file input webkitdirectory attribute to recursively list directory contents.

Use the file input button in the top-left corner to select a directory to visualize. The visualizer tries to generate a model of the full directory tree, so it only works on small dir trees (less than 500 files or so). Use the console to navigate by clicking on file names. You can also use some shell commands like ls, cp, rm and mv. The filesystem contents aren't modified though, only the visualization.

Here's a video of the visualizer in action from the Google I/O 2011 "HTML5: The Wow and the How"-presentation:

2011-08-19

Ken Burns effect using CSS


The Ken Burns effect is a special effect used in documentaries when you only have a static photograph of an interesting item. To add some movement and life to the photograph, you zoom into the photo and pan towards a point of interest. It's named the Ken Burns effect because it was used a lot by a documentary film maker named Ken Burns.

Anyhow.

You can achieve the Ken Burns effect using CSS animations. It's not even particularly difficult. Just create a div with overflow:hidden to hold the image, then change the image's CSS transform property. Or if you want to be totally retro and backwards-compatible, you could also achieve the effect by changing the image's top, left, width and height using a JS setInterval.

So, CSS:

.kenburns {
overflow: hidden;
display: inline-block;
}
.kenburns img {
transition-duration: 5s;
transform: scale(1.0);
transform-origin: 50% 50%;
}
.kenburns img:hover {
transform: scale(1.2);
transform-origin: 50% 0%; /* pan towards top of image */
}


And the corresponding HTML:

<div class="kenburns" style="width:640px; height:480px;">
<img src="image.jpg" width="640" height="480">
</div>


If you hover over the image, it will slowly zoom in and pan towards its top edge.

You can see the effect in action here. And a more complex version with a JS-driven lightbox here.

The (quick, hacky) code is on GitHub.

2011-08-11

Spinner library


Ok, uploaded the spinner library to GitHub. There's a demo page too. It's a wee bit buggy though. I think the transition events are not firing when the page is hidden.

Even getting it to the current phase was a pain, the interaction between CSS transitions and animations, the DOM, JavaScript and events is flaky at the edges. Once I get an animation or transition going, it works well. But if I want to change some CSS properties in JS with the intent to trigger an animation or transition, it's pretty much guesswork whether it will actually work without glitches.

Hmm... As a workaround, I think I could just create a new element on every show/hide-cycle. That way the animation can't possibly fire one 100% frame before starting from 0%, right?? (This happened on Firefox... or maybe it was an unanimated frame or something. I ended up switching from animations to transitions because of that.)

On Chrome Canary, border-radius and overflow:hidden don't behave like you'd expect. Instead of clipping the contents to the rounded box, it clips on the rectangular box. I don't know why. And sometimes the rendering flickers when using transforms that toggle HW compositing for the page.

2011-07-19

Story of Tomte's New Hat


To break the monotonic programmingification, here's a story about Tomte's new hat.

2011-07-06

Microbenchmark findings

Looping through canvas ImageData pixels: 1D traversal for (var i=0; i<data.length; i+=4) ... is slightly faster than 2D traversal for (var y=0; y<height; y++) { for (var x=0; x<width; x++) ... }. Caching the ImageData width, height and data properties to local variables is slightly faster than not (except in the 2D case, where it's a good bit faster). Stay away from crazy counting hacks, they're more likely to slow you down than speed you up (probably because they make the compiler's job harder and it can't optimize the code properly).

Based on the above, my preferred pixel loop format is:

var width = id.width;
var height = id.height;
var data = id.data;
for (var y=0; y<height; y++) {
for (var x=0; x<width; x++) {
var off = (y*width + x) * 4;
var r = data[off];
var g = data[off+1];
var b = data[off+2];
var a = data[off+3];
// ...
}
}

And if you just need to map over the pixels and don't care about the coords, the simple 1D loop:

var data = id.data; // or just use id.data directly, no big perf impact
for (var i=0; i<data.length; i+=4) {
var r = data[i];
var g = data[i+1];
var b = data[i+2];
var a = data[i+3];
// ...
}


Sparse blitting vs. dense blitting: If all you need to do is change the values of a small number of pixels, use fillRect. If you need to fill the entire canvas or a significant portion (say, 256x256) of it with custom data, use putImageData. There's no major performance difference between clearing an ImageData buffer before use vs. allocating a new one, so I'd go with clearing the buffer just to avoid extra GC work.

Text drawing: fillText is significantly faster than strokeText. Whether the text is aligned on an integer pixel only seems to matter on Opera's fillText.

Path drawing: Nothing very conclusive, path point count doesn't seem to have a major impact on path point throughput.

Spritesheets: For best performance, cache your spritesheet frames to separate canvases.

Image drawing: Align your images to integer pixels, don't transform them, use canvas elements instead of IMGs. Transformations perform very badly on non-accelerated canvases. Just offsetting an image by fractional pixels causes the browser to use the slow path. On accelerated canvases, transformations don't really matter all that much. You still get the best perf by doing aligned non-transformed draws though.

Clearing the canvas: Just use clearRect.

WebGL texture sources: Use premultiplied textures if possible. Use ImageData if possible, just don't specify it as premultiplied (same goes for typed arrays). Canvases are faster than images on Chrome, images are faster than canvases on Firefox. Typed arrays are about as fast as canvases.

Blog Archive