The quest for smooth scrolling

Back in 2012, when Facebook famously ditched HTML5 for fully native apps, the main problem they cited was a lack of smooth scrolling:

This is one of our most important issues. It’s typically a problem on the newsfeed and on Timeline, which use infinite scrolling. [...] Currently, we do all of the scrolling using JS, as other options were not fast enough (because of implementation issues).
— Tobie Langel

It should be clear why this was a dealbreaker for Facebook. If you want people to stay glued to their timelines, then you need a scrolling list that doesn't break a sweat. Otherwise the user's attention starts to drift pretty fast.

This affects more than just Facebook, though. As any mobile developer knows, the "ginormous scrolling list" is a common UI pattern across a wide variety of apps. Even if you're not Facebook, you probably have some kind of list – of notes, tasks, beers, Pokémon, whatever – that figures heavily into your app's presentation.

Recently Flipboard caused quite a stir by unveiling their scrolling list implementation, which renders to canvas in order to achieve 60 FPS. My reaction was: "neat!" The reaction of the web intelligentsia was a bit more mixed.

As an Android developer who only recently learned the web stack, I can see where Flipboard is coming from. I've heard for years that HTML5 was the equal of native platforms, and yet every time I've tried to write a PhoneGap or AppCache-powered web app, I've always had fun, but ended up feeling a little disappointed.

For instance, I recently built a prototype app using Ionic. Overall I found it to be a great developer experience, but getting a scrolling list to perform at 60 FPS turned out to be impossible. And this was despite the admirable efforts of the Ionic team, who developed some very clever tricks to achieve even 45-50 FPS.

So the Flipboard approach starts to look pretty attractive by comparison. The cry of the web advocates, however, is that we shouldn't sacrifice accessibility, copy-paste, "find in page," "open in new tab," or other classic browser features at the altar of 60 FPS.

My point of comparison, however, is with native apps. And when, exactly, was the last time you used a native app that let you "find in page"? Or even copy-paste? Sometimes you can "share," but such functionality is usually hobbled in comparison to the web experience.

Native apps have long abandoned the kind of built-in DOM features that web advocates swoon over (with the exception of accessibility, which works out-of-the-box with TextViews). And yet, native apps are the reigning champions of user attention, especially when it comes to "ginormous scrolling list"-style apps. That speaks volumes about what people really value when they're flicking through tweets at the bus stop.

The truth is that for a largely consumptive experience – which is what infinite scrolling embodies – 60 FPS is much more valuable than the interactive features like "find in page" or copy-paste. Admittedly, accessibility is harder to justify throwing under the bus, which is why even Flipboard acknowledged that they need to fix it.

But amidst all the hand-wringing from web apologists, as well as some gleeful jabs from the web's detractors, I found the most insightful response to the Flipboard fiasco to be Jacob Rossi's:

In other words, the web can solve the problem of slow scrolling in the same way that it solved the problem of slow animations. Just as CSS animations allowed us to achieve native-level performance by moving rendering off the main thread (where it blocks the DOM) and onto a background thread or the GPU (where it hums along nicely), the animation triggers proposal would move the responsibility for scrolling squarely to the browser.

Trying to re-implement the browser's scrolling in JavaScript, as Ionic and other frameworks do, is simply a non-starter if we want to achieve 60 FPS. JavaScript blocks the DOM, and as Flipboard says, "if you touch the DOM in any way during [JavaScript] animation, you’ve already blown through your 16ms frame budget."

Flipboard's solution was to replace the DOM with GPU-powered canvas. But the real solution is to replace JavaScript scrolling with GPU-powered scrolling.

For the time being, though, JavaScript scrolling is the best we've got, unless we want to dip into canvas or WebGL experiments like Flipboard's. Personally I think their approach is pretty exciting, especially if it spurs browser vendors to finally solve the sorts of scrolling problems that Facebook identified way back in 2012.

Of course, now Facebook has an even bolder experiment in React Native, based on the presumption that, even at 60 FPS, WebViews just "feel" wrong. But that will have to wait for a future blog post.

Closing the UX gap

For JavaScript to be an attractive tool for mobile developers, it's not enough for it to offer a good developer experience. It also has to offer a good user experience.

The UI should feel spry and peppy. Animations should zip. Screens should respond immediately to the user's touch. If there's a reason people love their phones so much, it's because mobile apps are very good at delivering on these promises.

Historically, though, hybrid and web apps have lagged behind their native counterparts in the "peppy" department. Mark Zuckerberg famously griped that HTML5 "just wasn't ready" – a phrase that still echoes in the minds of many mobile developers today.

For the mobile web to be taken seriously, it needs to provide the same smooth performance that we've come to expect from native apps. In short, it needs to close the UX gap.

Screenshot+2015-02-02+20.09.57.png

I recently gave a talk at Brooklyn JS where I shared some tricks for building web and hybrid apps with near-native performance. It wasn't recorded, but you can read the full slides and speaker notes online.

The slides themselves are kinda neat, because they contain the very CSS animations that I talk about in the presentation. So you can open it up on a mobile browser and see the difference between, say, hardware-accelerated and non-accelerated animations. The FPS demo is also fun to play with.

Just don't judge me too harshly if you open the slides in IE or Safari – I didn't have enough patience to get WEBM video or flexbox working properly. The CSS animations should look lovely, though.

Smooth animations in CSS

Working on the Squarespace Blog app for Android, one of the biggest challenges is that the app is half-native, half-web.

My job is to make sure you can't tell which is which.

Spoiler alert.

While trying to achieve this goal, one of the first things I learned is that animations with native-level performance – i.e. animations that don't stutter, jerk, or lag – can only be achieved with hardware-accelerated CSS.

Trust me, I've tried. The other methods don't even come close.

Here's a video showing a JavaScript animation, which I rewrote using hardware-accelerated CSS transitions:

And here's a regular, non-accelerated CSS transition, which I rewrote in the same way:

Behold the power of 3D transforms! And yes, I'm not actually using the z-axis, but it doesn't matter. Just consider "3D" to be the "open sesame" to coax the GPU into revealing its treasures.

If you'd like to get started with hardware-accelerated CSS, then you can take a look at this sample widget, which is the same one from the first video. It works on all modern browsers, both desktop and mobile. (Note the hand-crafted flexbox shim and -webkit prefixes.)

These videos were taken on a rather dated Galaxy Nexus running Android 4.2, so the performance improvement is crystal-clear. But in fact, you can also see the difference on more powerful devices, and even on desktop browsers. I actually first noticed the slow trashcan animation while playing with it on desktop Chrome.

Believe me: if it's anything less than 60 frames per second, your users will notice.