Bigshot VR with CSS 3D Rendering

Bigshot VR with CSS 3D Rendering

WebGL is cool, but not widely supported. Specifically, Apple's snazzy iDevices doesn't support it. Googling around a little I found the new 3D transforms that are up and coming in CSS 3. Without further ado, here's Bigshot VR using CSS[a], for those of you with browsers that support this (Apple's Safari, for example). The implementation will switch between WebGL and CSS depending on browser capabilities, so if you go to the demo page with a WebGL browser, you'll get the WebGL renderer.

Bigshot VR Demo[c]

The CSS renderer isn't as fast as the WebGL one, but it is more portable. It also makes the product sound like some kind of DSLR lens: Bigshot VR CSS 3D JS HTML 1.1 L for great pictures - it's what the pros use!

1. Notes on CSS 3D

CSS 3D Transforms Module Level 3[d] is one of those cases when an old dog is made to learn new tricks by "creative" means. In this case it is a head-on collision of the HTML DOM and a 3D scene graph. The results aren't pretty, but it can be made to work.

1.1. Positioning Elements

I first tried to use the transform functions[e] to position the tiles by translating and rotating them into position. That turned out to be a disaster. No matter how much I tried I couldn't get the tile to rotate around the axis I wanted.

Finally I gave up and used the matrix3d transform function. It allows you to transform an element using a 4x4 homogenous transformation matrix. That sounds complicated, but here's the short-short thing about it: If you know where in 3d-space you want to put the top left corner of the element, and you can figure out the 3d vectors that goes along the top and left edge of the elements, you can easily derive the matrix. Some vector math is required to understand this. Let's say you have an element that is 100px by 100px, and want to top left corner to be at P, with the top edge, going away from P, having vector U and the left edge vector V. First, U and V will be the new basis vectors for the element. They are therefore expected to be one pixel long. One step along either of them will take you one pixel to the right or one pixel down on the element. So we first divide U with the width of the element. Then we divide V with the height of the element:


U' = Uwidth


V' = Vheight

Then we form W as the cross product[f] of U' and V':


W=U' × V'

We can now write the matrix as:


M = Ux' Vx' Wx Px Uy' Vy' Wy Py Uz' Vz' Wz Pz 0 0 0 1

Which, when written as a transform function becomes:

matrix3d(U'x, U'y, U'z, 0,  V'x, V'y, V'z, 0,  Wx, Wy, Wz, 0,  Px, Py, Pz, 1)

1.2. World and View Transforms

I could never get my head around the CSS transforms. Finally I came up with the following:

<div id="canvas">
    <div id="canvasOrigin">
        <div id="view">
            <div id="world">
                Children of #world are 
                world-positioned elements

Using the following styles:

div#world, div#view {
    -webkit-transform-origin: 0px 0px 0px;
    -webkit-transform-style: preserve-3d;                
div#canvasOrigin {
    -webkit-perspective: 600px;
    -webkit-transform-origin: 0px 0px 0px;
    -webkit-transform-style: preserve-3d;
    left: 50%;
    top: 50%;
div#canvas {
    overflow: hidden;

This gave me something familiar: I'd set perspective on the #view div, do the view transform on #world, and use the positioning method above for the objects themselves.