Making of Starbucks Love Project

December 15th 2009



Over the past few weeks I've been busy working on the Starbucks Love Project website for Tool and BBDO.

I had the pleasure to work with a great team: Aaron Koblin, Hello Enjoy, Medios y Proyectos and We Are Mammoth.

During the development of my bits on the site I faced some little challenges that I though I would share so other people could consider my solutions as options for similar challenges.

The website had many release dates. This is something I though it was going to be hard to handle, but the guys at WAM managed it smoothly. However, the project had basically 2 phases.

Phase I - Love Gallery & Drawing Tool

My responsibilities for the first phase were to build the love gallery and the drawing tool.



The idea behind the love gallery was to collect love-related drawings submitted by the people, very similar to Aaron Koblin's The Sheep Market. Opposite to The Sheep Market, in this case there was no limit to the amount of submissions; that was already quite a challenge to start with.

The visualisation part wasn't decided at the start, interfaces like Google Maps (tiling) were considered, but in the end the guys at BBDO decided that it had to be similar to Cooliris. If supporting infinite amount of items wasn't challenging enough now we had to emulate what a some guys have done with OpenGL (hardware renderer) but with Flash (software renderer).

Instead of using Papervision3D or Away3D I decided to use my own engine as it was easier for me to tweak and remove computations that weren't needed. You can't have many 3d planes (specially not infinite) so the way it works is just by having 40x10 3d planes that offset the x positions depending on the camera position. Using their x,y positions I can figure out what's supposed to be their ID and then load the data from the nicely setup VO by the WAM guys. This sounds straight forward, but the code ends up being quite messy.

Loading lots of little images from the server was also another challenge. You spend more time asking the server for an image than receiving the image. To make this a bit more smooth we composed big images (Big Ass Images as the Americans liked to call them) with hundreds of little images.



So with just one request to the server we would receive lots of images inside an image. However, we did some bad decisions here. The dimensions of the big images are way too big (2800x2850) that it takes too much load time and affects the experience. And the one that pisses me off more is that I didn't realised in time (thanks anyway Mike!) that having those big images as .png or even .gif would have not only make them look better but also even have smaller filesize. Look at the images, look horrible! Oh well... next time.

Next was interaction. The people had to be able to select items on the 3d wall. That means projecting 2D points to 3D coordinates. That's something I didn't have on my engine, neither it was something for my mind to easily understand, so I took at look at how Papervision3D and others did it, and when I managed to understand and implement it, it wasn't working 100% well, there were some angle computations that had some delays and time was running out. So I went to the good old Color ID route.

What? Color ID? Yes, Color ID. What we're after here is to know what's 3D object is under the mouse. So in every frame we render the scene twice. Once using a big plane that covers the whole wall textured with a bitmap that has big pixels (that match each plane) and that has a unique color. Mapping the blue component with the X and the green component with the y... we will have... top left plane will have the color 0,0,0, second from the top 0,1,0, fist plane on the second column will have the color id 0,0,1 and so on.



This is how the first pass of the render looks. The user never sees this because as soon as it's done we copy it to a bitmap and then draw the real scene on top. If you look close you can see that the right of the wall is getting blue. That's because the Color ID is reaching 40. So having this in a bitmapData is just a matter of doing a getPixel(mouseX, mouseY) and we easily get the ID by using the coords get get and apply the camera offset. Then we know which plane we should animate/select. No complicated 3D to 2D formulas, just one getPixel per frame. Fast.

(Actually, after writing this I realise it could have been simpler by using the Plane ID as color instead of mapping the blur component to X and green component to Y... live and learn).

There was yet another little challenge. This was more a design challenge than dev, but somehow nobody managed to come up with a scrollbar that could scale to infinite amount of items. Once we started to have 15k drawings, moving the scrollbar one pixel would move the whole wall 100+ items which would mean a lot of movement and specially a lot of bandwidth because the user reached an area were more items needed to be loaded. The solution got implemented at the end of Phase II after I found this article.



The drawing tool was an easier task. Specially because for another project I had a drawing tool started and many of the challenges were already sorted. Explaining all the tricks used on the tool would require an article by itself, so maybe I'll talk about it as soon as I finish the other project.

Phase II - Map & Video Wall

For the second phase the site was going to collect videos sent from many countries where people would sing to the 'All you need is Love' track. (I wonder what's with this song, I'm hearing it in every AD campaign this Christmas, maybe it's an AD trend). My tasks for this one was to make a Map and another infinite wall (this time 2D).



Here I had a good idea. We needed to place arrows where countries are. An easy way would have been doing a little tool so whoever could place them. But if we had to change the shape of the map (which we did), we would have had to remap the arrows. Instead we used longitude/latitude values for each country (which are easy to find out on the net).

Usually there are 2 kind of world maps. One that has the poles distorted (Equirectangular projection) and one that hasn't (Mercator Projection, like Google Maps). The first one is good when mapping a sphere, but please stop using it on 2D interfaces.

Only thing to find out was the formulas to translate latitude/longitude to X,Y. The formula for the Equirectangular one is easy:

private function getPoint(lat : Number, lon : Number, mapwidth : uint, mapheight : uint) : Point
{
	return new Point(((lon+180) / 360) * mapwidth, ((90-lat) / 180) * mapheight);
}

The Mercator one took a bit more to find out, but just for the sake of having it here easy to reuse in AS3:

private function getMercatorPoint(lat : Number, lon : Number, mapwidth : uint, mapheight : uint) : Point
{
	return new Point(((lon+180) / 360) * mapwidth, ((90-GudermannianInv(lat)) / 180) * mapheight);
}

private function GudermannianInv(lat : Number) : Number
{
	var sign : Number = Math.sin(lat) > 0 ? 1 : -1;
	var sin : Number = Math.sin(lat * 0.0174532925 * sign);
	return sign * (Math.log((1 + sin) / (1 - sin)) / 2) * 28.6478898;
}

Having this sorted out, as long as the designers didn't distort the map the mapping of arrows would be always easy task.

At this point I needed to add animations. Coincidentally, eaze was just being released so I used this as a excuse to give it a try. Verdict, perfect.



Then it was yet again going back to optimising a wall that would have infinite entries. However, I didn't stress it too much this time because there were 200 walls instead of just one, so the amount of items would spread along these making it less CPU intensive.

Used again eaze to animate everything and again proven to be great.

All in all, it was quite a big project done in a very short time and I think we were lucky that we all had quite a bit of experience, otherwise we wouldn't had been able to pull this one off.

I couldn't finish this extremely-long post without sending some props to the WAM guys for their DoneDone. tool which I hope they make free for public projects at some point. It proved very efficient. Think of it like the Basecamp of Issue trackers.
16 comments written so far...

Great work doob. I'm jealous. Also, thanks for talking about your process a bit.
December 15th 2009
Alan
Nice work, going to check out eaze now. woot!
December 15th 2009
aaron
I believe it wasn't easy to finish!
Especially when you have the clock ticking.
As always great job!
(¡da gusto ver tus cosas, enhorabuena!)

Saludos! :)
December 15th 2009
Jorge
Hey doob :) Love it!

We did a project with many similarities - but on perhaps a smaller scale - http://www.tailsforwhales.org/

Sadly, no PV3D - and because of some project restrictions in good old AS2. I'd love to rip it apart and rebuild in AS3, but that's for another day.

Keep it real :)
December 15th 2009
Dan
By the way, the "flash" part of that site is at http://www.tailsforwhales.org/whale-gallery/
December 15th 2009
Dan
That was a fascinating read doob. Some of your solutions were really clever, like the color id thing.
December 15th 2009
Theo Denovan
Thanks for the feedback guys! :) Hopefully I'll be writing more making of from now on. As long as I learn something that I think it's worth sharing...

@Dan: It does look similar indeed, now put a 3d wall on it! ;D
December 15th 2009
mr.doob
Awesome.
December 16th 2009
Arturo
was totally amazed by the technical madness of the endless 3d wall. when I first viewed it i thought this thing is gonna just make my laptop explode! Somehow you managed to make this thing work without making my cpu melt. you did a great job parsing those large thumbgrids. I thought there is no way he's loading 10,000 images... I was right. A quick look at safari's activity window showed there were very few server requests. This is probably one of the most technically challenging flash executions i've seen. kudos to you and your team.
December 16th 2009
carl
Well, now you're going too far ;) For proper technically challenging flash stuff, check this out:
http://www.hobnox.com/index.1056.en.html
December 16th 2009
mr.doob
I love the post project writeup!!

I've heard of the color hit test thing before. I saw that in a 3D driving game for collision detection. Brilliant!!
December 16th 2009
hebchop
Great writeup.
Could it be that the color data is missing in the 4th image ?
December 17th 2009
Kris
No no, color data is there. It's just not human-eye friendly ;)
December 17th 2009
mr.doob
Grande!
December 18th 2009
Odrakir
Hat's off man!!!!!

One apreciation.
If do not missunderstood, about colour ID, and after analyzing (eyedropper tool) the mrdoob_sbuxluv07.png :
An invisible second pass texture with solid-colored quads like this
0,1,1 ... 0,1,255 - 1,1,1 ... 255,1,255
0,2,1 ... - 1,2,1
0,3,1 ... -
.
.
0,10,1 ... 0,10,255 1,10,1 ... 255,10,255
(10x256)x256
So it might be, about 655.360 different plane ID's limitation.
I've got it right?
Beyond this point, you could consider applying modulus 10 operation against the Green component.
December 19th 2009
JaK
There is no need for that. Remember, the grid of planes is always 40 (blue) x 10 (green).
December 19th 2009
mr.doob

Have your say!

Name:

Website:

Comment:

Some of the projects that I worked on.



Some of the HTML5 and Actionscript experiments I've done.