54

Does JavaScript provide a high resolution timer?

I've written a few game engines from scratch, some in C, some in Java, and some in Flash. I always follow the same basic model when it comes to animations and interactive graphics. Create a basic class/structure with the following design:

void init() { /* Called once, preload essential resources here. */ }
void update(double time) { /* Updates game/animation state using high resolution time. */ }
void render(double time) { /* Updates screen graphics using high resolution time. */ }

void run()
{
    double time;
    init();
    while (!done)
    {
        time = queryTime();
        update(time);
        render(time);
    }
}

Time is so important to smooth animations and game state calculations. In native code Windows, I use QueryPerformanceCounter() and QueryPerformanceFrequency() to perform the role of queryTime() in each game loop and pass the time to update/render. In Java, I use System.nanoTime().

What's the equivalent in JavaScript? That is, some function like queryTime() which returns a time value with a high degree of accuracy (sub millisecond). From what I've heard the best accuracy you can get in JavaScript is about 15 ms ... which is horrible for animation.

LJᛃ
  • 7,655
  • 2
  • 24
  • 35
sysrpl
  • 1,559
  • 3
  • 15
  • 24

11 Answers11

104

Almost all modern browsers provide a high resolution timer. It's the "High Resolution Time" W3C standard: http://www.w3.org/TR/hr-time/#sec-DOMHighResTimeStamp.

It allows you to get a sub-millisecond accurate timestamp by calling window.performance.now(). This returns a time stamp in ms, but it is a float so you still get sub-millisecond resolution.

Very old browsers may implement a "prefixed" version of this standard, e.g. WebKit based browsers used to implement window.performance.webkitNow()

Here is some code you can use to get the the accurate timestamp when available and fallback to standard precision otherwise:

if (window.performance.now) {
    console.log("Using high performance timer");
    getTimestamp = function() { return window.performance.now(); };
} else {
    if (window.performance.webkitNow) {
        console.log("Using webkit high performance timer");
        getTimestamp = function() { return window.performance.webkitNow(); };
    } else {
        console.log("Using low performance timer");
        getTimestamp = function() { return new Date().getTime(); };
    }
}

getTimestamp();

Note that this getTimestamp() function does not return a value that represents the current date/time. The returned value can only be used to measure time-periods, by subtracting two different timestamps. E.g.

var t1 = getTimestamp();
//... some other code
var t2 = getTimestamp();
console.log("Time delta: " + (t2 - t1));
h3r3
  • 1,164
  • 1
  • 8
  • 7
  • 1
    As of March 2014, Chrome's `window.performance.now()` still only provides millisecond resolution. See Oleg's answer. – Antimony Mar 04 '14 at 18:30
  • 3
    'Looks like Chrome `performance.now()` has microsecond resolution now. – broofa Nov 02 '14 at 03:42
  • 1
    Note that this value is not actually high-resolution due to Spectre and other threats. [Firefox, for instance, rounds to the millisecond.](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now). – Heretic Monkey Jan 28 '19 at 14:32
11

See @h3r3's answer—it is the correct answer.

Milliseconds are the best you can hope for in JavaScript. And, like you said, it isn't very accurate. See Stack Overflow question Microsecond timing in JavaScript.

timer.js purports to provide up to microsecond resolution, but it is only available for Google Chrome.

Update: timer.js does not support microsecond resolution. It just multiplies millisecond count by 1000.

Sorry, there isn't better news!

faintsignal
  • 1,828
  • 3
  • 22
  • 30
mwcz
  • 8,949
  • 10
  • 42
  • 63
  • 1
    Okay follow up. If browsers don't support accurate timing, how do people promoting html+javascript+canvas expect developers to create nice animations, or real-time interactive graphics for browsers using html+javascript+canvas? Animations should be based on time and not frame number. Basing animation on frame counts causes them to run at different speeds on different hardware/system configurations. It's also not as smooth. With time you can interpolate deltas. – sysrpl Jul 29 '11 at 15:52
  • 1
    To the best of my knowledge, Flash does not have sub-millisecond timing either. I don't think anyone is expecting equivalent multimedia performance to a native application, but rather a step in the right direction. – mwcz Jul 29 '11 at 15:56
  • 6
    Well it's nice to know that timer.js at least does that much, multiplication by 1000 is super tough. – theeggman85 Mar 22 '13 at 01:30
  • looks like we're getting closer in 2017: https://developer.mozilla.org/en-US/docs/Web/API/Performance/now ... granted the High Resolution Time API opens up the potential for some sophisticated cache attacks so it's sort of a blessing and a curse: http://www.cs.columbia.edu/~simha/spyjs.ccs15.pdf – Jordan Feb 15 '17 at 18:13
10

Instead of while (true)/setInterval, use recursive requestAnimationFrame. It will run smoother than timeout based animations. It provides timestamps if you need your animation to run at slower path.

katspaugh
  • 17,449
  • 11
  • 66
  • 103
  • Okay, thanks for that information. My code was pseudo-code to explain the structure of how I typically design games. It was good enough to garner your good response. Thanks. – sysrpl Jul 29 '11 at 16:04
6

As of now (February 25, 2013), the quality of high-performance times in Chrome 24 is pretty horrible.

var old = 0; 

for (var i=0; i<10; i++) {
    var t = window.performance.now(); 
    var d = t - old; 
    old = t; 
    console.log(t + ' += ' +d); 
}

outputs something like this:

609689.000000013 += 609689.000000013
609689.9999999441 += 0.9999999310821295
609689.9999999441 += 0
609689.9999999441 += 0
609689.9999999441 += 0
609690.9999999916 += 1.0000000474974513
609690.9999999916 += 0
609690.9999999916 += 0
609691.9999999227 += 0.9999999310821295
609691.9999999227 += 0

Which shows that

1) Sampling happens rarely

2) The precision is still around 1ms, not in the microsecond range.

vbo
  • 13,583
  • 1
  • 25
  • 33
Oleg Kikin
  • 411
  • 4
  • 10
2

Because this comes up when searching for JavaScript and high resolution timer, it is worth noting that window.performance.now is now functional (at least in Google Chrome v. 26) and provides ~microsecond resolution.

var d = 0;
var p = window.performance;
for(var i=0; i<10; i++) {
    d = -1 * (p.now() - p.now());
    console.log(d*1000);
}

It gave me this (in microseconds):

5.0000089686363935
3.9999722503125668
1.00000761449337
1.00000761449337
1.00000761449337
1.9999861251562834
1.9999861251562834
1.00000761449337
1.00000761449337
0.9999785106629133

I ran some stats on a few sets of 10k+ results. Minimum is about 1 microsecond, and the mean is about 1.25 microseconds on my machine (MacBook Air). Occasionally there are high outliers at the 100+ microsecond mark, but there were frequent results above 10 microseconds.

So the high-resolution timer is now capable of timing subtraction at microsecond resolution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
underrun
  • 6,713
  • 2
  • 41
  • 53
2

Here's what your code should look like in JavaScript, so it doesn't block the user interface and doesn't use window.requestAnimationFrame which does not work cross-browser.

/* Called once, preload essential resources here. */
function init() {}

/* Updates game/animation state */
function update(time) {}

/* Updates screen graphics  */
function render(time) {}

window.onload = function()
{
    var time;
    var done = false;

    init();
    // Using setTimeout passing zero makes the animate function run
    // as soon as possible, but yielding first.
    setTimeout(animate, 0);

    function animate () {
        time = new Date();
        update(time);
        render(time);
        if (!done) {
            setTimeout(animate, 0);
        }
    }
}

A problem with this approach is that the animate() may get called more often than the screen gets updated (at 60 Hz it won't update more often than abot every 16 ms), causing extra rendering that never makes it to the screen. That's why you should stick with requestAnimationFrame if possible.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • By the way, when you pass the delay of `0` to `setTimeout`, it's actually no zero delay. In Firefox 5 it's 4 ms, for instance. See `dom.min_timeout_value` in `about:config` settings. – katspaugh Jul 29 '11 at 16:16
  • @katspaugh: I made it pretty obvious in my answer that the zero means as soon as possible. Good info about the about:config setting. – Ruan Mendes Jul 29 '11 at 16:20
  • WARNING: `setTimeout` will still block your UI. The JS context is single-threaded, unless you explicitly launch a [web worker](http://www.w3schools.com/html/html5_webworkers.asp). – Domi Apr 18 '14 at 14:29
  • 1
    @Domi Any code that touches the DOM blocks the UI. The OP suggested a busy loop, that blocks the UI completely and never gives control back to the UI thread which updates the screen. Every time you call `setTimeout`, you're giving the UI thread a chance to update the screen, which is the point of an animation. Web Workers don't have access to the DOM, so you'd have to work with messages to get the DOM updated. – Ruan Mendes Apr 18 '14 at 14:52
1

Node.js has a high resolution timer, too.

process.hrtime()

See the documentation for details

Fox32
  • 13,126
  • 9
  • 50
  • 71
1

Just now I search for such solution, and found this thread. As I can see, all measurements done incorrect, because nobody know how work console.log function. From my experience (and practice) it take too much time, and even work asynchronously.

Just compare this code samples:

var old = 0; 

for (var i=0; i<10; i++) {
    var t = window.performance.now(); 
    var d = t - old; 
    old = t; 
    console.log(t + ' += ' +d); 
}

and this

var old = 0; 
var out = [];
for (var i=0; i<10; i++) {
    var t = window.performance.now(); 
    var d = t - old; 
    old = t; 
    out.push(t + ' += ' +d)
}
console.log(out);
degr
  • 1,559
  • 1
  • 19
  • 37
1

You can use CSS 3 transforms for simple animations that will get full hardware acceleration and will run silky smooth on most modern browsers... If you're expecting smooth animations on WebGL you're pretty much out of luck, because there's no submillisecond precision on JavaScript.

Currently, there's a lot of progress on enabling game related technologies for the web (see for example the mouse lock draft which is being actively developed our the fullscreen API... Maybe you can start the movement for microsecond precision timers in JavaScript ;-)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chiguireitor
  • 3,186
  • 1
  • 20
  • 26
0

Besides the already provided excellent answers, you may also have a look at marky, a

high-resolution JavaScript timer based on performance.mark/measure (461 bytes min+gz)

It also includes dev tools visualizations and is tested in a broad range of browsers.

Blacklight
  • 3,809
  • 2
  • 33
  • 39
0

Node supports nanoseconds via process.hrtime.bigint(), added in v10.7.0.

From the documentation:

The bigint version of the process.hrtime() method returning the current high-resolution real time in nanoseconds as a bigint.

Unlike process.hrtime(), it does not support an additional time argument since the difference can just be computed directly by subtraction of the two bigints.

import { hrtime } from 'process';

const start = hrtime.bigint();
// 191051479007711n

setTimeout(() => {
  const end = hrtime.bigint();
  // 191052633396993n

  console.log(`Benchmark took ${end - start} nanoseconds`);
  // Benchmark took 1154389282 nanoseconds
}, 1000);
Lee Goddard
  • 10,680
  • 4
  • 46
  • 63