Beercamp: An Experiment With CSS 3D

Advertisement

I recently had the pleasure of organizing this year’s Beercamp website1. If you’re unfamiliar, Beercamp is a party for designers and developers. It’s also a playground for front-end experimentation. Each year we abandon browser support and throw a “Pshaw” in the face of semantics so that we can play with some emerging features of modern browsers.

This year’s experiment: a 3D pop-up book á la Dr. Seuss. If you’ve not seen it, hop on over2 and take a look. The website was a test to see how far SVG and CSS 3D transforms could be pushed. I learned a lot in the process and wanted to share some of the techniques that I found helpful when working in 3D space.

3
“Beercamp 2012: A Tale of International Mischief”

Before we jump in, please note that explaining everything about the website without boring you to death would be damn near impossible. For your sake and mine, I’ll provide just brief takeaways. As you skim through the code snippets, be aware that jQuery is being used and that a lot of code has been removed for simplicity (including browser prefixes).

Finally, please remember that this is an experiment! It will not work in all browsers. It does not degrade gracefully, and the markup is less than poetic. Put your convictions on hold for a moment and let’s have some fun.

Takeaway #1: Exploring 3D Space Is Fun

Before I started building the Beercamp website, I did some “research” into what makes pop-up books so much fun. As I flipped through the paper-crafted version of Dr. Seuss’ Oh, the Places You’ll Go, I found myself inspecting each page from multiple angles. Seeing how things looked from different perspectives was fun, and interacting with the environment was engaging.


The inspiration for Beercamp: Dr. Seuss’ “Oh, the Places You’ll Go.”

I wanted to create that same engagement in my digital version with intuitive and unobtrusive controls. Thus, the scene rotates based on the mouse’s coordinates, allowing the user to move the book around without much effort. Achieving this was pretty easy:

1. Set up a listener.

This is for the mousemove event.

$document.mousemove(rotateScene);

2. Calculate the rotation.

I wanted the book to rotate between -15 and 15 degrees, based on where the mouse is located along the x axis. This can be calculated using the following:

rotationY = -15 + (30 * e.pageX / $body.width());

3. Apply the rotation.

$scene.css('transform': 'rotateY(' + rotationY + 'deg)');

Pretty simple, right? The only problem is that our friends on iPhones and iPads don’t have mouse coordinates. They do, however, have a gyroscope. Rotating a phone is very similar to rotating a book, so adjusting the scene based on the device’s orientation made for an intuitive and delightful interaction. Setting this up was similar but slightly more involved.

1. Set up a listener.

window.addEventListener('deviceorientation', rotateScene, false);

2. Determine the orientation.

Before we can calculate the rotation, we need to know whether the device is in landscape or portrait mode. This can be determined by evaluating window.orientation:

  • Landscape
    Math.abs(window.orientation) == 90
  • Portrait
    window.orientation == 0


Determine the device’s orientation by evaluating window.orientation.

3. Calculate the rotation.

Now that we have the orientation, we can pull in the appropriate values from the gyroscope. If the device is in landscape mode, we’ll tap the beta property. Otherwise, we’ll use gamma.

var theta = (Math.abs(window.orientation) == 90) ? e.beta : e.gamma;
rotationY = 0 + (15 * (theta / -45));


The deviceorientation event enables us to pull alpha, beta and gamma rotation values. Note that these values are relative to the current orientation of the device. The image above shows the axes of a phone held perpendicular to the ground in portrait mode.

4. Apply the rotation.

$scene.css('transform': 'rotateY(' + rotationY + 'deg)');

Takeaway #2: Depth-Sorting Is Notoriously Buggy

A number of browsers support 3D transforms, but few do so elegantly. Apart from general efficiency issues, the biggest hindrance is improper depth-sorting.

Depth-sorting is required when two planes intersect in three-dimensional space. The rendering engine must determine which plane (or, more specifically, which areas of the plane) should be rendered and which should be clipped.


Depth-sorting varies across browsers.

Unfortunately, each browser implements depth-sorting differently and, therefore, has its own issues. The best we can do to combat the glitchy pop-through of underlying elements is to keep planes away from each other.

The Beercamp website involves numerous plane intersections. Initially, I had all of the pages rotating around the same point in 3D space (0, 0, 0). This meant that just about every plane in the book was fighting to be on top. To counter this, the pages needed to be positioned as if they were next to each other along the spine of an actual book. I did this by rotating the pages around an arc, with the open page at the pinnacle.


Rotating pages around an arc helps to prevent clipping.

function updateDrag(e) {
    …
    // operate on each spread
   $('.spreads li').each(function(i) {
        // calculate the angle increment
        var ANGLE_PER_PAGE = 20;

        // determine which slot this page should be turned to
        var offsetIndex = per < 0 ? 5 + curPageIndex - i : 5 + curPageIndex - i - 2;

        // calculate the angle on the arc this page should be turned to
        var offsetAngle = per < 0 ? offsetIndex - per - 1 : offsetIndex - per + 1;

        // calculate the x coordinate based on the offsetAngle
        var tarX = 5 * Math.cos(degToRad(offsetAngle * ANGLE_PER_PAGE + 10));

        // calculate the z coordinate based on the offsetAngle
        var tarZ = 5 * Math.sin(degToRad(offsetAngle * ANGLE_PER_PAGE + 10));

        // position the page
        $(this).css('transform', 'translateX(' + tarX.toFixed(3) + 'px) translateZ(' + tarZ.toFixed(3) + 'px)');
    });
}

This technique helped to clear up most of the depth-sorting issues, but not all of them. Further optimization really relies on the browser vendors. Safari seems to have things worked out on both desktop and mobile. Chrome Stable struggles a bit, but the latest Canary works wonderfully. Firefox does a fine job but suffers from slow frame rates. It’s a tough battle to win right now.

Takeaway #3: Vector Space Is Tricky But Useful

Building the pop-ups was by far the most difficult aspect of the project, but also the most satisfying. Other pop-up books have been built on the Web, but I’m unaware of any that use realistic pop-up mechanics. This is with good reason — achieving it is deceptively complex.

The magic of programming pop-up mechanics lies in the calculation of vector space. A vector is essentially a line. Knowing the lengths and directions of lines enables us to perform operations on them. Of particular use when building pop-ups is the vector cross product, which is the line that runs perpendicular to two other lines in 3D space.

The cross product is important because it determines the upward rotation of each pop-up piece. I’ll spare you the headache of play-by-play calculations (you can view the math below if you’re really interested). Instead, let’s try a visual representation.


The vector cross product in action.

We start by determining two points where each pop-up piece touches the page within 3D space. Those points are used to define a vector for each pop-up piece (the red lines). Using those vectors, we can calculate their cross product (the blue line), which is essentially the line at which a physical pop-up folds in half. Rotating each piece up to the cross product then gives us perfectly aligned pop-ups!

This is not exactly easy math in my opinion, but it is extremely useful. If you’re interested in playing with vectors, I strongly recommend Sylvester94. It really simplifies vector math.

function setFold() {
    var points = [];

    // origin
    points[0] = [0, 0, 0];

    var adj = Math.sqrt(Math.pow(POPUP_WIDTH, 2) - Math.pow(POPUP_WIDTH * Math.sin(degToRad(-15)), 2));

    // left piece: bottom outside
    points[1] = [-adj * Math.cos(degToRad(-180 * fold)), adj * Math.sin(degToRad(-180 * fold)), POPUP_WIDTH * Math.sin(degToRad(-15))];

    // right piece: bottom outside
    points[2] = [adj * Math.cos(degToRad(-180 * 0)), POPUP_WIDTH * Math.sin(degToRad(-180 * 0)), POPUP_WIDTH * Math.sin(degToRad(-15))];

    // left piece: top inside
    points[3] = [-POPUP_WIDTH * Math.cos(degToRad((-180 * fold) - 90)), POPUP_WIDTH * Math.sin(degToRad((-180 * fold) - 90)), 0];

    var len = Math.sqrt(Math.pow(points[1][0], 2) + Math.pow(points[1][1], 2) + Math.pow(points[1][2], 2));

    // normalize the vectors
    var normV1 = $V([points[1][0] / len, points[1][1] / len, points[1][2] / len]);
    var normV2 = $V([points[2][0] / len, points[2][1] / len, points[2][2] / len]);
    var normV3 = $V([points[3][0] / len, points[3][1] / len, points[3][2] / len]);

    // calculate the cross vector
    var cross = normV1.cross(normV2);

    // calculate the cross vector's angle from vector 3
    var crossAngle = -radToDeg(cross.angleFrom(normV3)) - 90;

    // transform the shape
    graphic.css('transform', 'translateY(' + depth + 'px) rotateZ(' + zRot + 'deg) rotateX(' + crossAngle + 'deg)');
}

Takeaway #4: SVG Is Totally Tubular

I know, I know: you’ve heard the case for SVG before. Well, you’re going to hear it again. SVG is an incredible technology that works really well in 3D space. All of the illustrations on the Beercamp website were done in Illustrator and exported to SVG. This provided numerous benefits.

Benefit 1: Size

Because the pop-up pieces required large areas of transparency, the file-size savings of SVG were enormous. PNG equivalents would have been 200 to 300% larger than the uncompressed SVGs. However, we can reduce file size even more by exporting illustrations as SVGZ.

SVGZ is a compressed version of SVG that is incredibly small. In fact, the SVGZ files for Beercamp are up to 900% smaller than their PNG equivalents! Implementing them, though, requires some server configuration. This can be done easily with an .htaccess file:

AddType image/svg+xml svg svgz
AddEncoding gzip svgz

Benefit 2: Flexibility

The flexibility of SVG is perhaps its most highlighted benefit. The graphics on the Beercamp website are scaled in 3D space to fill the browser window. There are also hotspots on each page that allow the user to zoom in for more details. Because everything is handled with SVG, the illustrations remain crisp and clean regardless of how they’re manipulated in 3D space.


SVG files are inherently responsive.

Benefit 3: Self-Contained Animation

All of the SVGs on the Beercamp website are implemented as background images. This helps to keep the markup clean and allows images to be reused in multiple locations, such as with the pop-up pieces. However, this means we lose DOM access to each of the nodes. So, what if we need some animation on the background SVGs?

SVG allows us to define animations within the file itself. All of the pop-up images in the final Beercamp website are static, but an earlier version featured animated beer bubbles. To increase performance in some of the less-capable browsers, these were taken out. However, the SVG animations ran very smoothly in WebKit.

SVG animation gets less hype than its CSS cousin, but it’s just as capable. Within an element, we can add an animate node to specify typical animation settings: properties, values, start time, duration, repeat count, etc. Below is an excerpt from one of the Beercamp bubbles.

<circle fill="#fff" opacity=".4" clip-path="url(#right-mug-clip)" cx="896" cy="381" r="5">
    <animate attributeType="XML" attributeName="cx" from="890" to="881" begin="7s" dur="5s" repeatCount="indefinite" />
    <animate attributeType="XML" attributeName="cy" from="381" to="100" begin="7s" dur="5s" repeatCount="indefinite" />
</circle>

Takeaway #5: Experimentation Is Messy But Important

Now that the practical tidbits are out of the way, I’d like to say a word about experimentation.

It’s easy to get boxed in by the reality of developing websites that are responsive, cross-platform, cross-browser, gracefully degrading, semantically perfect, progressively enhanced, _______, _______ and _______ (space to fill in upcoming buzzwords). These techniques are useful on production websites to ensure reach and consistency, but they can also limit our creativity.

I’ll be the first to admit it: the Beercamp website is buggy. Browser support is limited, and usability could be improved. However, the website is an experiment. It’s meant to explore what’s possible, not satisfy what’s practical.

A dogma is emerging in our industry — and the buzzwords above are its doctrine. Experimentation enables us to think beyond that dogma. It’s a wonderful exercise that indulges our curiosity, polishes our talent and ultimately advances our industry. If you’re not experimenting in some capacity, you should be.

The State of CSS 3D

CSS 3D has yet to hit a tipping point. Browsers simply don’t support it well enough, but there is promise on the horizon. Mobile Safari, with its hardware acceleration, renders 3D transforms extremely fast and with very little depth-sorting issues. It’s only a matter of time until other manufacturers release stable implementations. It’ll be interesting to see how CSS 3D techniques hold up against other emerging technologies, such as WebGL.

Remember Flash? Me neither.

You’re Invited

By the way, Beercamp is being thrown by nclud5 at the Front-Trends Conference116 in Warsaw. If you’re headed to the conference, you should stop by and say hello!

(al)

Footnotes

  1. 1 http://2012.beercamp.com/
  2. 2 http://2012.beercamp.com/
  3. 3 http://2012.beercamp.com/
  4. 4 http://sylvester.jcoglan.com/
  5. 5 http://nclud.com
  6. 6 http://2012.front-trends.com/
  7. 7 http://24ways.org/2010/intro-to-css-3d-transforms
  8. 8 http://www.w3.org/TR/SVG/animate.html
  9. 9 http://sylvester.jcoglan.com/
  10. 10 http://2011.beercamp.com/
  11. 11 http://2012.front-trends.com/

↑ Back to top Tweet itShare on Facebook

Tom Giannattasio happily makes things at nclud. He works as an Editor for Smashing Magazine and teaches at Boston University Center for Digital Imaging Arts. He loves to experiment and share his work on his personal site: attasi.

Advertisement
  1. 1

    Jens Grochtdreis

    April 18, 2012 11:28 pm

    @Donovan: No, I didn’t miss the paragraph. I did get the message and I didn’t miss the point that this site is an experimental one. Nevertheless I critisize, that the experiment was done with pure ignorance for other than two browers.

    If someone feels it important to do such an experiment it is okay for me. But as I am often teaching developers in modern web development I always emphasize on “think in alternatives”. In this case there are none. And this is bad.

    Why should I be forced to use a special browser to view the content of this page? If I only want to know the date and location of the beercamp I am totally lost with the page. It behaves strangely and has a bad performance (MacBook Pro, Chrome 20 dev). With Opera or an IE I couldn’t even reach the basic infos. That’s my point.

    Such an experiment can lead uninformed developers and designers to false conclusions. Conclusions I always try to get out of their heads. It is because of that I find experimental pages like that so bad. The page would be good if the developer had thought in alternatives. In reality he did the same mistakes we critisize of all those intranet-apps that were programmed only with IE6 in mind. The IE6 of today in this wrong approach is called “Webkit”.

    -6
  2. 52

    Really amazing.. thanks for sharing the article, was a very inspiring read.
    To me, the beercamp website is the best and most innovative site I’ve seen in 2012 so far.

    4
  3. 103

    I agree with you that JavaScript is not really the greatest language around, but when you say “Not ActionScript”, I would like to know why? Are you referring to AS 2 or AS 3? Do you even know this languages? Do you have any other language on your mind, that you would rather use?

    I’m getting rather sick of people commenting about web technologies that they never used. If flash page is running slow it’s not the problem with the technology but with the person who coded it. The same goes for “HTML5″ pages that heavily rely on experimental features that are implemented by just a handful of vendors. If you open such a page in the “wrong” browser, you get broken content or sometimes even no content at all. But nobody goes around screaming that HTML5 (whatever that means) is broken and that we should all just forget about it.

    And as for your last sentence that “time is money for developers”. If you design your page like you, developer, are the only person that matters, than you are doing it wrong. Countless flash apps were made with the same attitude and look at the perception that people have about it. If you want to be a good developer, than you have to know the basics of the language you are using. I think it is a really bad practice using frameworks just because it is “faster” to code something, if you don’t know how they work or what are they actually doing. You just end up adding a ton of code to your webpage, so you can have a nicely animated button. And when it comes to stuff like 3D, there is no shortcuts if you want your stuff running on anything that isn’t an 8 core gaming rig.

    So yes, you should care about pure JS if you are doing your stuff for anyone other than yourself.

    0
  4. 154

    well. it works (chrome on a 6 year old macbook), but: it’s slow and nearly not able to use. so, it looks nice, yes, but – sorry to say – fail..

    1
  5. 205

    I like this a lot; its great on Safari. Thank you for your article.

    0
  6. 256

    Thank you for the fantastic website and this helpful article. They are totally amazing!!

    I have translated this article into Chinese, and published it in this blog site: http://fed.renren.com/archives/786.

    By the way, Can you add a link to our article in your orginal blog? Maybe this will help thousands of Front-End developers in China to read and improve. Thank you very much!

    [ :-) Note : If you are interested in our work, you are welcomed to visit our website: http://www.renren.com & http://fed.renren.com ]

    0
  7. 307

    I have never heard of 3D CSS before, I find this to be extremely helpful in my search for new ways CSS can be used. However, is it compatible with all browsers, and is it compatible with portable devices or computers?

    0
  8. 358

    Wow.. This is super awesome. Beercamp’s page last year (2011) is also awesome. But this is more awesome than ever. I have add this super awesome site into WDIT’s collection.

    http://webdesigninspirationtoday.com/design/396/beercamp-2012

    This is really everyone’s inspiration. This site showed great implementation of modern technology on the web. It uses CSS3, Javascript, and also SVG(!). thanks for this beautiful site!

    0
  9. 409

    Has anyone pushed the technique further, a navigable interactive 3d map for example? Looking for tutorials or examples to learn from.

    -1

↑ Back to top