Distortion Grid using CSS 3D Transforms #

I was recently wondering if it'd be possible to re-create the hottest demo of 2000 (specifically, the Mac OS X genie effect) inside a browser. More generally, it would be neat to have a grid-based distortion system. It would certainly be possible by drawing things inside a <canvas> and then applying distortions pixel-by-pixel (e.g. in the way that The Wilderness Downtown corrected for distortion). However, I was hoping to use CSS 3D Transforms so that actual application of distortions would be hardware-accelerated in browsers that supported it (<canvas> hardware acceleration is coming soon, but isn't quite here yet).

I then came across Wonder WebKit, which reminded me that it's possible to directly specify a matrix to use via (WebKit)CSSMatrix, and also provided a port of OpenCV's getPerspectiveTransform to JavaScript.

Mandrill distored

A couple of days of hacking later, I have a demo of distortion grids in action. It requires Safari 5.0 or Chrome dev channel (or other channels with accelerated compositing enabled). There's also a short screencast showing all the features in action.

The grid control points are drawn as plain DOM nodes, and made made draggable by handling mouse events (there's also basic touch event support, but multi-touch is not supported). The grid is rendered via a <canvas> overlay. The source data is divided up into tiles, each tile being defined by the four control points at its corners. When a point is moved, the perspective matrix that transforms from the source coordinates to the distorted ones is recomputed. When holding down option/alt, nearby points are also moved (less and less, with a 1.3^(Manhattan distance) decay factor). When holding down shift, other points are moved to maintain the overall aspect ratio.

A few different datasources are supported. The most obvious is an image, which is subdivided into tiles by drawing sections of it into smaller canvases. Iframes are also supported, albeit not in a very elegant fashion. The source iframe has to be cloned (and therefore loaded) once per tile. Something like the moz-element extension would allow the iframe to be drawn into each tile without actually having to clone it. Most interestingly, movies (via the <video> tag) can also be used as a source. They are treated quite similarly to images (each frame is drawn into a canvas, and then pieces of that are drawn into tiles). Maintaining 30 frames per second doesn't seem to be a problem, since once the matrices are set up, most of the video playback and transformation can be hardware-accelerated.

Unfortunately I couldn't quite replicate the full animated genie effect. Though it is possible to snapshot transformed tiles and use CSS Transitions to animate between then, the interpolation between matrices doesn't behave quite as expected, and seams appear. It therefore seems like animation would have to be done by hand, with control point positions being interpolated and then matrices being recomputed every frame. Especially with a finer grid, maintaining 30 fps was deemed more trouble than it was worth (a.k.a. I got lazy).

4 Comments

Awesome post...and cool work with the distortion grids.

I've managed to reproduce the genie-effect in browser...when finished, I'll release a js-lib but since it's not finished yet, I've posted a youtube-clip of the work in progress:

http://www.youtube.com/watch?v=UwUxo-R-mzU
i can use with jquery???
Why not? Eventually, it can be integrated with jQuery as a plugin...even though it should be lib-independent. There are a few issues left...not working in Safari is the biggest one.

Check out my notes here:
http://www.vanguard-ide.com/blog/

...and here is the repo:
https://github.com/hbi99/genie.js
Genie.js is wonderful! How can it be reversed so image expands out from the top of the window?

Post a Comment