We here in the basement of Enlighten’s engineering labs were recently tasked with a unique challenge: to create an engaging animation composited from a user’s Facebook photos—on a cross-browser, cross-device basis. The fine print of this challenge also included the following requirements:
- Don’t make the user wait around – playback should feel almost instantaneous.
- Target the fastest, smoothest performance possible across desktop/laptop, tablets and other mobile devices.
- Stick to a single code base as much as possible, to streamline maintenance.
- Support some ancient browsers.
- Launch it really, really soon.
I know what you’re thinking: such solutions only exist in two places:
- In the wildest dreams of implementation engineers
- In pitch meetings that the tech lead was “invited to, I swear – I don’t know what happened. It must be something with Outlook. Anyway, I know this is an aggressive schedule….”
Naturally, we sized up the challenge before us, didn’t complain at all, and dove right in – and I’m here to tell you that we found the solution. And by “found” I mean: we kind of saw it once, walking through a clearing, and then it ran away:
Much like the guy who shot the above film (Roger Patterson, in case you’re wondering), I will happily tell you all about the hunt – and not just because I can see that you want to read several pages worth of sardonically documented engineering decisions. NAY, I will tell you… out of necessity.
You see, I have one final bigfoot metaphor prepared: While we’ve come out the other side of this experience with the memories of what we saw, we are unfortunately left with a complete lack of physical evidence. We’d love to point you to the site so that you could inspect the result, but we’re in one of those John Hughes-ian, “Yes, we’re dating but you can’t tell anyone we’re dating” situations. All we have to show for our adventure are a bunch of metaphorical plaster casts of oversized tracks.
But you believe me, right?
Instantaneous, composited animation
Ok, so let’s take this one step at a time. Requirement one: After a user authenticates via Facebook connect, we need to pull images from her account and instantaneously display them composited into an animation of some sort.
Serving up a rendered video file would certainly simplify delivery across all of our targets – mobile-accessible video delivery is something we do on nearly every project – but the “instantaneous” requirement means that we’re not going to be able to take the user’s photos and render them into unique video files on the server.
Fair enough. If we’re not serving up a video file, we’re talking about some sort of animation, and there are actually several options:
- HTML5 Canvas element
- CSS3 Animation
For this project, Flash was off the table. We wanted to avoid using it in order to preserve iOS compatibility. Based on the schedule and the wide range of target platforms, we had a pretty strong feeling we’d be using some flavor of HTML5 or CSS3.
We created a series of test pages, each using one of the remaining methods listed above to perform the background animation for the final piece. Using these as a basis for comparison, we attempted to gauge performance across a wide range of our targets.
It immediately became clear that CSS3 animations were the best option for iOS (and that standard DOM manipulation was the worst). But the “single code base” requirement drove us toward selecting the HTML5 Canvas element, as it offered the most consistent performance across the widest range of our targets.
Ok, cool, so we’re going with Canvas. Hm? Oh, right, browser support! Our first hurdle! This project needed to adhere to our existing matrix of supported browsers, which included the latest version of all webkit browsers (as well as FF3.6 for folks who love ignoring update messages), and Internet Explorer all the way back through IE7 (mercifully, IE6 was recently dropped). This list didn’t mesh with the Canvas-based solution we arrived at, as neither Internet Explorer 7 or 8 support the HTML5 Canvas element.
What this meant was we were able to write the code to animate something using the HTML5 Canvas element, and then use that same code to control the animation in Internet Explorer 8 and older, where the Canvas element technically doesn’t exist.
Hooray! A single codebase, IE 7 and 8 supported! But not so fast, as there are a couple of caveats to using FlashCanvas as a fallback:
- Since FlashCanvas displays things using Flash, all the fun cross-domain restrictions apply, so if you plan on pulling in images or files from other sources (as we pulled images from Facebook), you’ll need to set up a proxy page. The installation comes with a PHP proxy, but if you’re on a different platform, you’ll need to write your own.
- Custom font rendering isn’t built into the current release. If you dig around on the message boards, you’ll find works-in-progress that support this, but we found them to have a few issues. We ended up only using standard fonts when displaying using FlashCanvas.
- Our particular animation was extremely image-intensive. Through trial and error over the course of the project, we discovered that a number of hiccups and visual artifacts we were seeing were caused by the first very frame of drawing a new image to the screen. We ended up drawing all images used throughout the entire course of the animation frame in every frame. If a particular image wasn’t used in the current frame, it was drawn just offscreen. All the visual defects went away. Your mileage may vary.
The right tool for the job?
At this point, we had internally arrived at a creative direction involving a background and several foreground layers scrolling horizontally in parallax. Our next step was to begin to prove this concept out using some of the leading “HTML5 Animation” tools. We created proofs of concept using each of the following:
Why does it keep doing that thing?
Still reading? Ok, all the cool kids are definitely gone, we can relax.
In olden times, the setInterval function was used to repeatedly generate frames for an animation, but performance considerations could sabotage this method, making the resultant framerate vary wildly.
Unfortunately, we were seeing collisions and “weirdness” that may or may not have been caused by requestAnimationFrame (Since the problems went away when we reverted away from it, it got the blame). Eventually the timeline won out and we stuck with that old standby setInterval to run the animation loop. Given more time, we’d definitely revisit this decision.
- Joe Lambert’s post on the beginnings of these matters.
- Erik Möller’s post on the polyfill.
Until next time?
Some great articles on this sort of thing can be found here:
- Eternal Coding Blog – “Unleash the power of HTML 5 Canvas for gaming”
Until then: keep your eyes on the hills.