Low Latency Mobile Web Apps

Low Latency Mobile Web Apps


Hi, I’m Andrey and I’m an
engineer on Google News team. I’m here to talk
about some things we learned about building
performant web UI while building the
Google News phone and tablet mobile web apps. I’ll describe how
we worked around the shortcomings
of mobile devices and took advantage of
modern browser features and ultimately delivered
engaging and fast news experiences. Mobile devices are rapidly
becoming the dominant platform to access online
content and services. Rapid growth of mobile devices
present a great opportunity, but also come with their own
set of technical and product challenges. Technically, mobile
devices generally suffer from slow and unreliable
networks, limited memory, relatively weak CPU and GPU,
and, of course, limited screen size. At the same time, most mobile
devices have modern OSs and have browsers which are
HTML5 compliant with features like local storage
and touch events. This opens new ways to
create engaging web apps. One of the most prominent
challenges of phones is slow network connections. This means that phone apps
should limit data consumption to as little as possible
on the connection. Now, this may prompt
developers to retrieve as little data as possible
in one data connection and make frequent data calls. This approach could
backfire, as opening a connection is quite
expensive and time-consuming. So a balance needs
to be maintained between data transfer on one
connection and opening multiple connections. On Google News, we balance this
by getting all the textual data of the phone web app in
one call to the server. Images are fetched later
on– as they are not critical to the news
reading experience– as are various scripts
and resources which are not immediately needed. Another workaround
to slow connections is to optimize data access
around product usage patterns. In Google News,
we try to predict what the user would do next
and prefetch that data. This means we can
show content to users as quickly as possible. For example, when a user
swipes to a section, it’s a good indication
that user could swipe to the next section
of our app as well. In that case, we prefetch
the next few sections while the user is active
on the current section. Another technique that
we use is to fetch partial concept of
a page beforehand. For example, we retrieve
the first article of all news stories while
fetching a section page data. When the user navigates
to a story page, we display the first
article immediately while fetching other
articles on the news story. We’ll also show a silhouette
instead of an empty white space while fetching the
rest of the story page. This gives users some content
to interact with immediately and indicates to the
user that more content is being retrieved. Static resources such as
JavaScript, CSS, and sprites can be stored in local storage. Thus, the application
will avoid making HTTP HEAD calls to verify
that the cached browser version of the file
is good to use. This saves a few round
trips to the server. So these are some of the
transport layer considerations, but what about mobile
browser level optimizations? Well, the [INAUDIBLE]
rendering of that by browser internals
in the CPU and GPU have many more
deficiencies compared to typical desktop or
laptop machines today. You must be careful when
manipulating and accessing properties of DOM
elements, as it may result in full
or partial screen delay in the [INAUDIBLE]. Use Chrome Dev
Tool’s timeline view to see what the browser spends
time doing frame by frame. And you can turn on various
experimental settings that show internal drawing and
[INAUDIBLE] of the browser. When moving HTML5
elements on the screen– especially in the response to
user gestures– use HTML5 3D transforms. This will offload [INAUDIBLE]
processing to the GPU, resulting in a much
smoother experience. GPU memory is quite
limited, so try to move only the elements
which are visible and hide the invisible
sides of elements. For elements that
are about to appear on the screen– like when a
pane is about to be swiped onto a screen–
it’s very helpful to bring them into the DOM
structure well ahead of time. And make them
renderable, meaning, you need to set CSS display property
to something other than none. Browsers are typically capable
of performing layout and pane of several screen elements
on a separate thread, often prioritizing among
different elements based on distance to visible viewport. If you absolutely need
to animate something with JavaScript, use the
RequestAnimationFrame API callbacks to synchronize
DOM changes with the vsync. Everything we’ve
talked about so far is also good practice when
developing other non-mobile web applications. But one key feature that
singles out mobile applications is that they are controlled
through gestures. Natively, browsers
support vertical swipes through the vertical scroll
of the main body element. But if you want your
application to respond to horizontal swipe or
other gestures like pinch, you’ll need to process raw
touch events in JavaScript and animate appropriate
parts of the UI. To do so, the
touchstart event should be processed in the
consecutive touchmove. And ultimate touchend
events should also be processed in this JavaScript. And this is where
it gets tricky. For UI to appear
smooth, all transitions should run at about
60 frames per second which leaves about 60
milliseconds per frame. The main thread that’s
responsible for reporting and executing application
specific touchevent handlers must complete script
execution and browser paning within those
60 milliseconds. What this means is that code
in touchstart– and especially touchmove handlers–
should complete in no more than a
few milliseconds. For comparison,
full-screen layout in pane might take 20 to 30
milliseconds resulting in several skipped frames. The thing that allows 40
panes to be quick is CSS 3D transforms, which move elements
on screen as raster images rather than trying to
repane DOM structure. Once you’ve optimized
touchevent handlers, you can do browser
level optimizations to streamline browser
event processing and reduce overhead associated
with event DOM propagation. One way to achieve this is
to listen to all touch events on a single top-level DOM
element in capture mode. This will result in no DOM
event propagation chipping valuable milliseconds
from touch event processing on the main thread. In our phone web app we
wrote a small framework that did just that. It processed raw touch events
at the top-level element, converted these touch events to
gestures– such as drag-left, drag-right, up and down–
and routed these gestures to the appropriate subcomponent
of the application based on subcomponent
reported [INAUDIBLE], for which it should be
notified of gestures. The last topic of this talk
is phone versus tablet UI. A common debate is how different
phone and tablet UIs should be. One school of thought is
to have responsive websites where a site is
automatically adjusted to the available screen
size and capabilities. In our experience, this
approach doesn’t work very well. First, a UI that adjusts
dynamically results in complicated code and
limits optimization choices. But more importantly,
what we have found is that phone and
tablet consumption models are very different. Phone consumption happens
more frequently in a day, but a session lasts
only a few minutes. This is what we call
the “snacking” mode of news consumption. This means it is much better
to have an information-dense and more summarized
view of news. Whereas tablet
usage is very much limited to morning
and evening hours with the user interacting for
an extended period of time. So it made much more sense
to provide in-depth reading choices on tablets. We hope this talk will help
you avoid some common pitfalls of web development
on mobile platforms and will inspire you to
code slick and fun web applications for
many users to enjoy. Thank you and see you
at news.google.com.

Danny Hutson

Leave a Reply

Your email address will not be published. Required fields are marked *