
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.
24 comments