How to accurately compute VSYNC timing information
from a noisy vsync signal source -- November 24, 2016

Getting a time 'around' the VSYNC time is easy: (1) In native code, obtain vsync times using various native methods (under Windows, DwmGetCompositionTimingInfo() is one possible way). OR, (2) in HTML5, there is a new requestAnimationFrame call, intended to allow animations to smoothly hook into web browser internals, which is already rendering frames for the display:
All web browsers attempt to synchronize animation callbacks to the actual display VSYNC signal, which allows us (via JavaScript) to discover the "tNow" time of each callback, which is 'usually' very near to VSYNC:
function rafcallback( tFrame ) {
  var tNow =; // this time is 'usually' very near VSYNC
So, the only information we have access to is an approximate/noisy "tNow" values -- and nothing else!
FYI: There is a "tFrame" time argument passed to the rAF callback. Most browsers simply pass in the now() time. However, Chrome passes in the vsync time obtained from the OS. Firefox passes in a completely faked time.
But the problem is NOISE!: But the huge problem is that the "tNow" times can be a noisy signal. Each "tNow" may have an ERR of up to several milliseconds (sometimes more!) and there will even be completely missing frames/times. Even the vsync times in native code can sometimes be noisy! In mathematical terms, the "tNow" the rAF callback sees is defined by the following equation, which is just the familiar line formula y=mx+b, but with an additional error term:
tNow = tInterval × nFrame + tTimebase + ERR
The goal: The goal is to create an algorithm that can extract/compute an incredibly accurate "tTimebase" and "tInterval" from the above formula using ONLY the numerous, but noisy, "tNow" times seen. The complication is the impact of the "ERR" term, missing frames, etc.
In Chrome, "ERR" can be up to 1 ms on fast systems, up to 2ms on slower systems, and up to 4ms on notebooks running on battery power.

In Firefox under Windows, the "ERR" term can (1) be all over the place, and (2) permanently phase shifted several milliseconds past true vsync. See Firefox is broken for details.
One solution: One solution (of many) can be found in the HTML source at in the "var HZ=" object. In summary, the algorithm is:
  1. Filter incoming tNow frame times: Discard times where a run of several inter-frame times are not 'stable'. The goal is to filter out all major jitter, leaving only times with minor jitter remaining (Y values). Also filter to account for (slow) browser startup/overhead, and being run in an inactive web browser tab.
  2. Compute a (one-time) first/rough "tInterval" from an initial set of filtered times.
  3. Every so often, compute a 'line of best fit' (needs X values, computed from smoothed times and a 'prior' "tInterval"), which generates a "tTimebase" and a new "tInterval" (for the next iteration).
  4. Adjust the computed "tTimebase" such that all "ERR" terms in the 'line of best fit' are non-negative.
  5. Validate the results. If there is too much variation in the "ERR" terms, start over.
  6. Allow the algorithm to be run over an extended period of time, while still using a fixed memory and cpu overhead.
Algorithm validation: Chrome is the only web browser that (reliably) passes an accurate true vsync time to the rAF callback (as the time argument). displays this as the 'late' line in the performance graph (in purple). When displayed against the "tTimebase" and "tInterval" line the algorithm computed (in blue), there is a perfect match (the two lines nearly overlap, but with an very tiny and expected phase shift), which validates that the algorithm works:
Conclusion: The algorithm in practice works incredibly well. It is able to 'lock into' the VSYNC "tTimebase" and "tInterval" with a high degree of accuracy in very little time (within seconds).
Use rAF time: If running Chrome on Windows, visit, click on the gear icon, check the "Use rAF time arg as frame time" option, which will then feed the vsync time Chrome obtained from the OS into the algorithm -- and watch how quickly an accurate Hertz value is computed.

Reducing ERR: Regardless of how many frames there are, the ERR term is added at the end -- which means that the ERR is effectively spread over that many frames. So the more frames you sample, the smaller the error per frame becomes, and the more accurately "tInterval" will be computed. And then the best fit line formula finds a (consistent) 'path' through the 'ERR' jitter that further reduces the 'ERR' impact.
Analogy: What is the height of ONE quarter? Measure one quarter and you will be off by 'ERR'. But now stack 100 quarters and measure all 100. Your measurement will still be off by 'ERR'. But then you divide your answer by 100 to obtain the height of one quarter, and you have effectively divided your 'ERR' by 100 as well!