Building a pure CSS 3D City

I was recently experimenting with CSS 3D transforms. Is it possible to build a 3D city with just CSS? Yesterday I’ve posted on twitter about a quick CSS 3D demo, here comes the follow up blog post.

Most of you are aware that Safari and most decent modern browsers support CSS rotation. No javascript nor plugins involved.

The following code rotates #element by 15 degrees.

#element {
	-moz-transform:rotate(15deg);
	-webkit-transform:rotate(15deg);
	-o-transform:rotate(15deg);
	transform:rotate(15deg);
}

Okay, admittedly, this is boring. What is really interesting is that Safari (mobile version included) and Chrome support transforms on a 3D matrix, opening an endless range of opportunities.

I was wondering if this could be used in a real world application. How many 3d objects can you place on screen before slipping in the dreaded mobile browser crash?

My task was to build a SimCity like pure CSS block. Buildings are easy, they are just cubes with a fancy texture.

Setting up the first building

The following is what we are going to achieve:

And this is the live demo. (modern wekbit only).

Each building is composed by 5 sides (we don’t need the bottom/ground side, as it is not visible).

The HTML will look something like this:

<div class="building">
	<div class="side1"></div>
	<div class="side2"></div>
	<div class="side3"></div>
	<div class="side4"></div>
	<div class="side5"></div>
</div>

We have 5 overlapping divs, now we need to rotate and translate them to obtain a cube.

Rotating side 1 by 90deg on the Y axis (-webkit-transform:rotateY(90deg)) we get this:

An useful CSS property is -webkit-transform-origin , with it we can change the origin of the rotation. Adding -webkit-transform-origin:100% 0; we get pretty close to what we are aiming:

But if we add a texture to the face we notice that the wall is in the wrong orientation.

We need to rotate the wall onto the Z axis and translate it a bit. We finally have the right side.

The end CSS is:

-webkit-transform:rotateY(90deg) rotateZ(-90deg) translateY(-100%);
-webkit-transform-origin:100% 0;

You can obtain the same result with transforms only, it is not needed to change the object origin. That’s up to you. A webkit-transform only solution might be like so:

-webkit-transform:rotateY(90deg) rotateZ(-90deg) translate3d(0,-50%,50px);

Now repeat for all sides. Watch the end result on any Safari or Chrome.

Performance

My main concern was about performance. How is this going to scale with -say- 16 or 32 buildings? Each element needs six divs to be rendered (five sides plus one container) and from my experience with iScroll I know that the iPad doesn’t like pages with too many elements.

So I multiplied the building by 16 and surprisingly the iPad is able to serve a smooth and frame rate steady animation.

The problem here is that we need 96 divs to build 16 boxes. Not elegant at all, so I tried a different approach.

Reducing DIVs count with a different approach

Have a look at this second experiment:

We have 6 buildings but the whole city is composed by 19 divs only (not 36), and placing more buildings doesn’t require adding more divs. With the very same installation you could have 16 buildings without touching the DOM.

It’s a bit tricky, but the following picture should unveil the mystery:

The red transparent layers are the actual DIVs. I have the same number of elements on the other axis of course. I didn’t use 6 elements per building, I just have a 8×8 grid of divs.

Setting up the city is just a matter of placing the right texture (background) in the right place, and –as CSS3 supports multiple backgrounds– your city is a few CSS properties away.

Here’s how the first DIV looks like:

background:url(front1.jpg) 0 100%,
	url(front1.jpg) 100px 100%,
	url(front1.jpg) 100px 0,
	url(front2.jpg) 300px 100%,
	url(front2.jpg) 300px 0;

And here’s the live demo (Safari or iPad/iPhone only, as always).

Now the problem is that, even if composed by fewer elements, this second experiment is far slower than the 6-divs-per-building solution. Maybe Safari webkit doesn’t like crossing transparent layers, or elements with multiple backgrounds. Either way if you are going to release the next full CSS3 SimCity or GTA, you better go with the first solution pictured in this post.

Does it really matter?

Why the hell are you doing this? You may argue. After all we have canvas with which people are releasing Quake Arena clones at 60fps.

First of all some devices and older PCs perform really bad with canvas. The iPad and iPhone for example do not really like it, while 3d transforms are hardware accelerated.

Secondly, we are more accustomed to work with DOM elements than canvas madness. This doesn’t mean that we shouldn’t use canvas, I’m just offering an alternative.

Future development

This experiment gave me a fair amount of new ideas for an iPad multiplayer game. If you want to stay with me, I’ll post the project progress here on my blog and later on the game’s site if things get serious. Stay tuned.

80 thoughts on “Building a pure CSS 3D City”

  1. Freaking awesome 🙂

    I’ve been following you on twitter the last past months. Pretty interesting. Keep on rocking!

  2. This is amazing, how about creating a javascript library to setup such a world, creating the css transfoms stuff and so

    1. Yes, I agree. A JS library is the only way to build a complex world. If this game is really going to happen I’ll surely release the framework under open source license.

  3. I was working on the exact same example a few months ago. While canvas is an impressive alternative to pure dom scripting, I was just curious how far this approach could be pushed.

  4. Sweet!
    The first demo works well in Chromium 12 as well, the second one didn’t unfortunately. Right now we’re at a split where Mozilla is betting all their 3D money on webGL, and safari, though webkit was the first engine to implement webGL, seems to refuse to put it in the mainstream releases. Honestly it seems webGL is more useful but i’d settle for any 3D technology that gets some wider adoption.

    Cheers!

  5. Great post.

    As you suggested, maybe not the best approach a lot of times, but it’s always really interesting to read about oft-ignored dusty corners of technology.. keep it up.

  6. Really cool stuff. I’ve been playing around with a similar thing myself recently, but wondering if something is possible (I can’t figure it out!):

    Say I made a 3d ‘box’, in the same way as your first example here. Is it possible to put the ‘viewport’ or ‘camera’ *inside* that box, so when I rotate the box, what the user sees is themselves inside the box, with the box rotating around them?

    If I translateZ the box towards the screen, the perspectiveOrigin stays in the same place, but if I move the perspectiveOrigin as well, then I can never be ‘inside’ the box, I’m always outside it but just really close up!

    Any ideas?

  7. Pingback: Build city | CHASD
  8. Awesome work!
    I want to create a rubik’s cube using divs, I have the 2D version but translating it into 3d was a mystery. I have got a idea thanks to this. Will give it a try

  9. Instead of saying “Safari” or “Safari and iPad/Phone” only, you should’ve just said the demos only work properly in most webkit browsers since that’s the rendering technology you’re using.

    Yes, I’m nitpicking, but it’s annoying that this many years later iFans are still going “Safari/i___ EXCLUSIVE!!!!!” and not understanding the technology behind it.

    Of course, saying “This demo working is most webkit browsers such as Safari” is perfectly true and acceptable.

    1. Dear Kat,
      when this demo was released the only browser supporting 3D translation was Safari (browser and mobile). Only few weeks ago Chrome added css 3d support.

  10. Pure awesomeness: I’ll apply it somewhere soon!
    Thank you for the great study you did! This experiment makes my life a lot easier!

  11. The first demo (modern wekbit only) doesn’t work on Chrome (current version). Just a heads up.

    I love the concept of this, great work!

  12. This is pretty awesome. I just started looking into alternatives for game development. Definitely something i want to start with.

  13. Very nice. I have been considering similar but with gradients or SVGs. I would love to add camera view as if a camera was moving around, zooming, turning etc. See my ‘Flight of the Venturer’ animation (see website linked) that shows the concept.

      1. Of course!
        I mean, was just as trivial as adding the typical browser manufacturer extensions.
        I wasn’t adding any browser-specific differences, since so far, there aren’t any.

      2. So, just FYI, looks like the generic extensions never made it into your demo.

        At some point hacks.mozilla.org decided to link to the link above.

        http://hacks.mozilla.org/2011/10/css-3d-transformations-in-firefox-nightly/

        (Search for CSS 3D city)

        Of course, they probably linked to that version since yours was still not cross-browser.

        BTW, I do believe the CSS transform stuff is now in Firefox Aurora which should be officially released around Christmas.

  14. BTW, Moz guys said that ordering like index.html relies on is undefined in spec, and webkit does different things on different platforms. But apparently Mozilla will have ordering that is closer to what webkit does, more or less, shortly…
    Hopefully I’m not putting words into their mouths there and I understood ’em correctly.
    Anyway, still awesome demos, esp index2.html !

  15. This is so awesome and very inspiring! 🙂 Can’t wait to start experimenting with this.

    Thanks for sharing!

  16. You should use kd tree depth sorting to handle depth-rendering artifacts.

  17. What’s up colleagues, how is the whole thing, and what you wish for to say about this article, in my view its truly awesome designed for me.

Comments are closed.