I finally found some time to finish my short series of tutorials on CSS 3D transforms. If you haven't checked out part 1 & 2 I recommend you to do so, otherwise you will feel lost pretty soon.

In this tutorial we will learn how to build a 3D packshot in HTML and CSS by applying some CSS 3D-transforms. Then we will add some Javascript to make the object freely rotatable in 3d space. And as we will enhance our Javascript with some touch-interactivity, the packshot will also work nicely in Safari for iOS-platforms like iPhone or iPad. A warning: be prepared that the second part of this tutorial will be quite math-heavy.

Open Example

...or direct your mobile-browser to www.eleqtriq.com/wp-content/static/demos/2010/rotation/ (sorry for the mobile-unfriendly URL).

The current State of CSS 3D

Since I published my first tutorial a few months ago, more and more sites showed up that actually use CSS3D transforms. But as we all have learned, support for CSS3D-transforms is still marginal outside the iOS world. So what is the use in learning this technique anyway?

Enough said, let's start the tutorial:

1. Building the packshot with HTML and CSS:

To demonstrate CSS 3D on a real world-example we will create a virtual packshot for Spritebaker. We start with a basic HTML-"sceleton":

<!-- We need something to apply some CSS 3D-perspective to it. Therefore we set up a section as our stage. --> <section id="viewport"> <!-- The next container is our box. It consists of 6 sections for every side: --> <article id="box"> <section id="front"> <!-- HTML for the front goes here--> </section> <section id="top"> <!-- HTML for the top goes here--> </section> <section id="bottom"> <!-- HTML for the bottom goes here--> </section> <section id="back"> <!-- HTML for the back goes here--> </section> <section id="left"> <!-- HTML for the left-side goes here--> </section;> <section id="right"> <!-- HTML for the inner-side goes here--> <!-- Just kidding :)--> </section> </article> </section>

In order to style the content we will use a handful of conventional HTML and CSS-techniques, therefore I will not bore you with too much details, you are a gown-up web-developer, right? Some lame 2005 image-replacement and CSS-sprites to make everything fairly accessible and crawlable (as you know I'm a big proponent of Data-URI-Sprites, but I avoid them in my demos most of the time to keep source-code readable).

2. Applying the CSS 3D Transforms:

More fun to come: let's form a nice 3D-box by rotating and positioning every side. The CSS transforms are straightforward and easy to understand:

section#viewport{ perspective: 700px; perspective-origin: 50% 50%; } article#box{ transform-style: preserve-3d; } #box section{ transform-style: flat; /*browsers are bitches at z-sorting css3d-transforms. Use backface-visibility: hidden as often as you can! */ backface-visibility: hidden; } #front, #back{ width:250px; height: 354px; transform: translate3d(0px, 0px, 37px); } #back{ transform: rotateY(180deg) translate3d(0px, 0px, 37px); } #top, #bottom{ width:250px; height: 74px; transform: rotateX(90deg) translate3d(0px, 0px, 37px); } #bottom{ transform: rotateX(-90deg) translate3d(0px, 0px, 317px); } #left, #right{ width: 74px; height: 354px; transform: rotateY(-90deg) translate3d(0px, 0px, 37px); } #right{ transform: rotateY(90deg) translate3d(0px, 0px, 213px); }

We add some spice by adding a webkit-box-reflect below and voila, ready is our box! Attentive readers will see that the reflection is erroneous, with the box-shape accurately reflected but not the typography on the box. This is a bug in webkit for MacOS. Applying a 3D-transform to an element causes that its content can't be reflected properly (strange to say this bug doesn't affect Safari for iOS).

3. Making the Box rotatable:

Figure 1: the Virtual trackball.

It's starting to get interesting: let's add some interactivity with Javascript so we are able to twist and turn the box with the mouse (side-note: we will avoid any Javascript-library like jQuery or Motools and code directly for Webkit instead, as its our sole target-browser. This way the script stays lean and small, <3kb gzipped without comments, mobile browsers will love you for that).

Some Words about Object-Rotation in 3D-Space:

Rotating and twisting objects in 3D-space might not sound like a big deal: just change the object's angle according to the mouse position on the screen, right? Unfortunately it's not that easy. The movement will feel unnatural and "bumpy" and we will have to deal with the problem of "Gimbal-Lock".

There exists a better technique for achieving a natural and smooth experience for the user. It's called the "Virtual Trackball". The Virtual Trackball is an imaginary sphere around our 3D-object. Every mouse-click on the screen will be mapped onto this virtual sphere and every dragging-operation will cause a rotation of the virtual trackball and the object inside, resulting in a smooth and intuitive rotation. Let's see how to build a virtual trackball for our box:

Mathematics of the Virtual Trackball:

How to construct the virtual trackball:

Figure 2: Mapping the coordinates of the mouse to the Virtual Trackball.

The center of the box and the Sphere must be at the center of the coordinate-system (y=0, y=0, z=0), which is also the center of the stage. The sole purpose of the virtual trackball is calculating angles in 3d-space. We are not interested in distances, it is enough to know their relation. Therefore we will put the sphere into an easy to calculate coordinate-system where its radius is simply "1". Mouse-coordinates on screen can be translated to this new coordinate system easily by dividing all values by the "untranslated" radius. Choosing the right radius is a matter of instinct. Choose a value that "feels right". I decided to make it half of the shortest side of the viewport. This maybe not o. k. in every situation though, you will always have to test and make your decision accordingly.

2. On mouse down, detect the mouse-coordinates and map them onto the trackball:

Figure 3: determining the rotation-axis from the vector at the moment of mousedown and the vector during rotation.

we translate screen-coordinates to trackball coordinates:


..and then move [0,0] to the center of the trackball coordinate-system:

x = x - 1;
y = y - 1;

We already know the radius of our sphere (it's 1, remember?) so it's quite easy to determine the z-coordinate of the translated point with the help of some good old trigonometry:


Now that we have everything in place, we can start building the rest of the logic. I will give an overview in pseudocode. One more remark: the script makes heavy use of 3d transformation-matrices and rotate3D, but avoids "rotateX, Y, Z". If we would use single-axis-rotations it would be necessary to calculate the rotation in quaternion-space to avoid gimbal-lock. If we use rotate3D or matrix3d, webkit will handle the quaternion stuff for us.

At first-run or at the moment the user releases the mouse (or lifts his hand) we create a new matrix3D from the last axis-vector and angle, store it in a variable and apply it to our box. When the user decides to click and rotate again, we will apply this startmatrix together with rotate3D to our box. This way we prevent our box from flipping back to it's initial, "unrotated" state on every mousedown/touchstart-event.

function init(){ set up the virtual trackball; search the viewport-html-element for something to rotate; add mousedown- or touchstart-listener to prepare rotation; add mouseup- or touchend-listener to finish rotation; add mousemove- or touchmove-listener to rotate; calculate initial matrix3d from initial angle and rotation axis; } function startrotation(){ track click-position and translate it to trackball; store resulting 3d-vector in variable "mouseDownVect"; } function rotate(){ track current mouse-position and translate it to trackball; store resulting 3d-vector in variable "mouseMoveVect"; find rotation-axis by determining normal on mouseDownVect and mouseMoveVect; find rotation-angle between mouseMoveVect and mouseDownVect; apply startmatrix and rotate3d(axis, rotation-angle) to the box; } function finishrotation(){ calculate new matrix3d from last angle and rotation axis; combine the last start-matrix and this new matrx to a combined matrix by multiplication; make current matrix3d new start-matrix; }

This is an overview of the actual logic. If you want to dig deeper, you should check out the Javascript, I gave my best to comment every step.

The script (I call it "traqball.js" by the way) is released under MIT- and GPL-license and you are alllowed to use it in your projects as long as you stick to the terms of license. Implementation is easy:

Ok, we're through! Hope you enjoyed this little lesson. If you have used the script in your projects I would be more than glad if you send me a link.

Update updated traqball.js to version2.0. Source now is on Github. Read more here..


42 Responses to “Natural Object-Rotation with CSS3 3D”

  1. Comment by waqas 04/23/2011

    Sigh, what I wouldn’t give to stop people from using stupid browser sniffing. Fails on chrome because Chrome versions are no longer single digit…

  2. Comment by Dirk 04/24/2011

    Actually you have 3 options here:

    1. You are a smart kid. Shouldn’t be too hard for you to figure out how to set up the browser sniffing for current versions of chrome.

    2. Wait until I put a new version online. Should happen within the next 2 weeks if I find the time.

    3. Build a version that works without browser sniffing (which isn’t that easy, I can tell you) and post the solution on your blog or here. This is much more constructive than hanging around on other peoples blogs and proclaim. And as a side efffect the community will actually profit from your input. Read Paul’s comment above for an example how to give constructive feedback the right way.

  3. Comment by Derek 07/29/2011

    How do you use the “traqball.js”? Can you make a tutorial of it?

  4. Comment by Derek 07/29/2011

    I don’t really understand what should put in the [a,b,c] and the “initial rotation-angle”… Please help…

  5. Comment by Dirk 07/30/2011

    Hi Derek,
    the first is an array representing the x-, y-, z-axis components, the last is an angle in radian. E.g. [0,1,0], 1.5707 will rotate the object 90deg around the y-axis. If you are undecided leave them out and default vals ([1,0,0], 0 ) will kick in.

  6. Comment by Derek 07/30/2011

    Thank you for your explanation! And I spent some time, and I figured out that the “traqball.js” is not working if I use “margin:0 auto” to center the cube.

  7. Comment by Derek 07/31/2011

    CORRECTION: Maybe it is not the “margin:0 auto” that causing it not working. I don’t know but when I add a “width” and “height” to the container, it started to work.

  8. Comment by ZhouQi 08/11/2011

    it’s magic!

  9. Pingback by CSS Summit 2011 – Notes from Day 1 08/14/2011

    […] Natural object rotations with CSS3 3D – very cool effect (click on image & use your mouse to move it around). […]

  10. Comment by Javery 10/14/2011

    You were just featured during the Web Directions 2011 in Sydney. You’ve got a room of over 100 people who are VERY impressed.

    Nice work!

  11. Pingback by New CSS Techniques and Tools | W Design Love | Graphic and Web Design Blog | Coding | tutorials | freebies | Wordpress | inspiration | Blog 10/16/2011

    […] Natural Object-Rotation with CSS3 3D A tutorial by Dirk Weber that teaches us how to build a 3D packshot in HTML and CSS by applying some CSS 3D-transforms. By adding some Javascript, we can make the object freely rotatable in 3D space. And as we will enhance our Javascript with some touch-interactivity, the packshot will also work nicely in Safari for iOS-platforms like iPhone or iPad. […]

  12. Pingback by Weekly Update 4 | Justin Avery Web Site Analysis Design Development 10/23/2011

    […] rotations…. it’s got most of what you might want (cause you never really need it) 3D Image transformation is one of the cool bits I learned while at the Web Directions South.  This is an awesome […]

  13. Comment by Paul Irish 11/05/2011

    Nice rewrite! Sexy conditional loading action. :)

    Unfortunately the new code has a bug. Looks like Chrome doesn’t like you not passing an argument to cancelRequestAnimationFrame().. I get TypeError: Not enough arguments

    Fix is easy enough. Capture the return value of rAF and then shoot it into your cRAF call. Already tested it out and it’s groovy. :)

    Btw we caught this because the demo broke onstage during @garazi’s talk at StarTechConf in santiago Chile. :p

  14. Comment by Dirk 11/07/2011

    *cough* – how embarrrrrassing o_O. Should work again now :-) Thanks Paul!

  15. Pingback by Robert’s read: links and suggestions November 16th 2011 - Robert's talk 11/16/2011

    […] Natural Object-Rotation with CSS3 3D […]

  16. Comment by Forkoff 02/03/2012

    Firefox now supports 3D transforms as of Version 10


  17. Comment by Stefan 03/02/2012

    Hi there,

    We are currently using the cube and are trying to make the images clickable for browsers AND the iPad.
    It works for the browsers only not on the iPad, perhaps I’m doing it wrong. We’ve used the code:

    It seems the only thing clickable on the iPad is the borders, we’ve also removed the images but still you can’t click on the planes. It there anything that we can do to make the images clickable on the iPad?

    Cheers (from Holland)

  18. Comment by mauritius 03/19/2012

    Awesome would really want to integrate this css 3d in one of my website but the browser compatibility issue really make this difficulf

  19. Pingback by GNC Designstudio 06/23/2012

    […] Link: Dirk’s rotation example […]

  20. Comment by Star 07/04/2012

    the coolest thing i’ve seen in my life .I think this the future of web technology.But ie please support on ie7 as well as ie8.I hope in the near future all browsers will automatically updates.With or without the consent from the owner.that would be awesome.I will apply your ideas into my website.hahah
    hope you will find it amusing