Aligned input events

Dave Tapuska
Dave Tapuska

TL;DR

  • Chrome 60 reduces jank by lowering event frequency, thereby improving theconsistency of frame timing.
  • ThegetCoalescedEvents() method, introduced in Chrome 58 provides the samewealth of event information you've had all along.

Providing a smooth user experience is important for the web. The time betweenreceiving an input event and when the visuals actually update is important andgenerally doing less work is important. Over the past few releases of Chrome, wehave driven down input latency across these devices.

In the interest of smoothness and performance, in Chrome 60, we are making achange that causes these events to occur at a lower frequency while increasingthe granularity of the information provided. Much like whenJelly Bean was releasedand brought theChoreographerwhich aligns input on Android, we are bringing frame aligned input to the webon all platforms.

But sometimes you need more events. So, inChrome 58, we implementeda method calledgetCoalescedEvents(), whichlets your application retrieve the full path of the pointer even while it'sreceiving fewer events.

Let's talk about event frequency first.

Lowering event frequency

Let's understand some basics: touch-screens deliver input at 60-120Hz and micedeliver input typically at 100Hz (but can be anywhere up to 2000Hz). Yet thetypical refresh rate of a monitor is 60Hz. So what does that actually mean? Itmeans that we receive input at a higher rate than we actually update the displayat. So let's look at a performance timeline from devtools for a simple canvaspainting app.

In the picture below, withrequestAnimationFrame()-aligned input disabled, youcan see multiple processing blocks per frame with an inconsistent frame time.The small yellow blocks indicate hit testing for such things as the target ofthe DOM event, dispatching the event, running javascript, updating the hoverednode and possibly re-calculating layout and styles.

A performance timeline showing inconsistent frame timing

So why are we doing extra work that doesn't cause any visual updates? Ideally, wedon't want to do any work that doesn't ultimately benefit the user. Starting inChrome 60, the input pipeline will delay dispatching continuous events(wheel,mousewheel,touchmove,pointermove,mousemove) anddispatch them right before therequestAnimationFrame() callbackoccurs. In the picture below (with the feature enabled), you see a moreconsistent frame time and less time processing events.

We have been running an experiment with this feature enabled on the Canary andDev channels and have found that we perform 35% less hit tests which allows themain thread to be ready to run more often.

An important note that web developers should be aware of is that any discreteevent (such askeydown,keyup,mouseup,mousedown,touchstart,touchend) thatoccurs will be dispatched right away along with any pending events, preservingthe relative ordering. With this feature enabled, a lot of the work isstreamlined into the normalevent loopflow, providing a consistent input interval. This brings continuous eventsinline withscrollandresize eventswhich have already been streamlined into the event loop flow in Chrome.

A performance timeline showing relatively consistent frame timing.

We've found that the vast majority of applications consuming such events have nouse for the higher frequency. Android has already aligned events for a number ofyears so nothing there is new, but sites may experience less granular events ondesktop platforms. There has always been a problem with janky main threadscausing hiccups for input smoothness, meaning that you might see jumps inposition whenever the application is doing work, making it impossible to knowhow the pointer got from one spot to the other.

ThegetCoalescedEvents() method

As I said, there are rare scenarios where the application would prefer to knowthe full path of the pointer. So to fix the case where you see large jumps andthe reduced frequency of events, inChrome 58,we launched an extension to pointer events calledgetCoalescedEvents(). Andbelow is an example of how jank on the main thread is hidden from theapplication if you use this API.

Comparing standard and coalesced events.

Instead of receiving a single event, you can access the array of historicalevents that caused the event.Android,iOS,andWindowsall have very similar APIs in their native SDKs and we are exposing a similarAPI to the web.

Typically a drawing app may have drawn a point by looking at the offsets on theevent:

window.addEventListener("pointermove",function(event){drawPoint(event.pageX,event.pageY);});

This code can easily be changed to use the array of events:

window.addEventListener("pointermove",function(event){varevents='getCoalescedEvents'inevent?event.getCoalescedEvents():[event];for(leteofevents){drawPoint(e.pageX,e.pageY);}});

Note that not every property on the coalesced events is populated. Since thecoalesced events are not really dispatched but are just along for the ride, theyaren't hit tested. Some fields such ascurrentTarget, andeventPhase will havetheir default values. Calling dispatch related methods such asstopPropagation()orpreventDefault() will have no effect on the parent event.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2017-06-22 UTC.