Three.js: Clock (start, getDelta) broken in Node.js r74, 'performance.now' not defined

Created on 12 Feb 2016  路  15Comments  路  Source: mrdoob/three.js

The functionality of THREE.Clock broke in release r74 when attempting to run in Node.js (installed via npm package 'three'). Functions like .start() & .getDelta() complain about ReferenceError: performance is not defined (as in performance.now()).

I might be missing something, but it would seem the changes in 71ddb80818c504028a0cd1dd4c61de0edaa237a8 removed the polyfill for performance.now necessary to run THREE (Clock) in Node; as far as I can tell, Node.js doesn't have a global performance implemented.
I am not sure if/how much issues #7787, or #7800 are involved.

Running w/ r74 (current broken state):

$ node
> var THREE = require('three');
undefined
> var myclock = new THREE.Clock();
undefined
> myclock.start()
ReferenceError: performance is not defined
    at THREE.Clock.start (/data/users/rlinsalata/workspaces/js_ws/node_playground/node_modules/three/three.js:7911:20)
    at repl:1:10
    at REPLServer.self.eval (repl.js:110:21)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.EventEmitter.emit (events.js:95:17)
    at Interface._onLine (readline.js:202:10)
    at Interface._line (readline.js:531:8)
    at Interface._ttyWrite (readline.js:760:14)
    at ReadStream.onkeypress (readline.js:99:10)
    at ReadStream.EventEmitter.emit (events.js:98:17)

> myclock.getDelta()
ReferenceError: performance is not defined
    at THREE.Clock.start (/data/users/rlinsalata/workspaces/js_ws/node_playground/node_modules/three/three.js:7911:20)
    at THREE.Clock.getDelta (/data/users/rlinsalata/workspaces/js_ws/node_playground/node_modules/three/three.js:7938:9)
    at repl:1:10
    at REPLServer.self.eval (repl.js:110:21)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.EventEmitter.emit (events.js:95:17)
    at Interface._onLine (readline.js:202:10)
    at Interface._line (readline.js:531:8)
    at Interface._ttyWrite (readline.js:760:14)
    at ReadStream.onkeypress (readline.js:99:10)
> THREE.REVISION
'74'

Running w/ r73 (old working state):

$ node
> var THREE = require('three');
> THREE.REVISION
'73'
> var myclock = new THREE.Clock();
> myclock.start()
> myclock.getDelta()
20.876
> myclock.getDelta()
1.437
> myclock.getDelta()
4.405

Version Info:
Three version r74
npm package 'three' [email protected]
Node v5.2.0
Ubuntu 14.04

Most helpful comment

+1

THREE.Clock doesn't work on iOS 8 (Chrome and Safari).

All 15 comments

Hmm, is using THREE.Clock a common thing on node.js? What's your use case?
Maybe a solution would be to add a polyfill for your project?

For me, not working on iOS8 based devices. I have added this r73 polyfill and works again:

//

if ( self.performance === undefined ) {

    self.performance = {};

}

if ( self.performance.now === undefined ) {

    ( function () {

        var start = Date.now();

        self.performance.now = function () {

            return Date.now() - start;

        }

    } )();

}

@rethink-rlinsalata As you can see here, the High Resolution Time API is widely supported in all major browsers. So it's of course comprehensible to remove the respective polyfill.

I would use in a node.js environment not a polyfill, but the more accurate process.hrtime() API. Check out the npm package present, which is built on top of process.hrtime.

+1

THREE.Clock doesn't work on iOS 8 (Chrome and Safari).

The way the values are currently being processed adds up numerical error.

I guess we might as well just use Date.

0.001 is periodic in binary (check the bits here). This error can be minimized dividing by 1000.

Further, the digits before the dot in elapsedTime eat up precision the higher the value gets. I guess the performance.mark method might be the intended way to avoid this problem.

I guess the performance.mark method might be the intended way to avoid this problem.

Good suggestion. I just wonder how performant is performance.mark and performance.measure when we use it in an animation loop. If we want to calculating the elapsed time with performance.measure, we always need two marks. The API also recommends to clear obsolet marks and measures which are stored by the browser.

@Mugen87

I just wonder how performant is performance.mark and performance.measure when we use it in an animation loop.

Maybe we just shouldn't. Milliseconds are actually quite alright when it comes to timing animations.

The API is for benchmarking, for which we'd have to

  • stop using random all over the place, and
  • use a clock that can be switched to provide discrete time (based on a frame counter) for timing the effect

in order to get reproducible results.

use a clock that can be switched to provide discrete time (based on a frame counter) for timing the effect

Sounds interesting. @tschw Do you know some references or examples about this topic? I would really like to learn something about this...

@tschw

use a clock that can be switched to provide discrete time (based on a frame counter) for timing the effect

@Mugen87

Sounds interesting. @tschw Do you know some references or examples about this topic? I would really like to learn something about this...

Not as much interesting as it sounds, I guess. I meant: Take a fixed start value and always increment the same constant every frame - just as you would do for rendering to a video file.

If you want to know how a particular, possibly small change affects the performance, you must have a bit of scale and make sure you always measure the same process.

I see! Things are more clear for me now. :+1:

Still this error. r84

image

@Mugen87 ( performance || Date ).now() results to error in node.
( typeof performance === 'undefined' ? Date : performance ).now() works.
https://github.com/mrdoob/three.js/blob/master/src/core/Clock.js#L23

@mrdoob I think we should implement @sasha240100 suggestion with a respective comment.

We use performance only in these two lines in the entire three.js core:

https://github.com/mrdoob/three.js/blob/master/src/core/Clock.js#L23
https://github.com/mrdoob/three.js/blob/master/src/core/Clock.js#L57

I was looking into this the other day - the equivalent to performance.now() in node is process.hrtime(). Unfortunately it returns an array [seconds, nanoseconds] 馃槖

Tween.js includes a polyfill. It's not that small though:

// Include a performance.now polyfill.
// In node.js, use process.hrtime.
if (typeof (window) === 'undefined' && typeof (process) !== 'undefined') {
    TWEEN.now = function () {
        var time = process.hrtime();

        // Convert [seconds, nanoseconds] to milliseconds.
        return time[0] * 1000 + time[1] / 1000000;
    };
}
// In a browser, use window.performance.now if it is available.
else if (typeof (window) !== 'undefined' &&
         window.performance !== undefined &&
         window.performance.now !== undefined) {
    // This must be bound, because directly assigning this function
    // leads to an invocation exception in Chrome.
    TWEEN.now = window.performance.now.bind(window.performance);
}
// Use Date.now if it is available.
else if (Date.now !== undefined) {
    TWEEN.now = Date.now;
}
// Otherwise, use 'new Date().getTime()'.
else {
    TWEEN.now = function () {
        return new Date().getTime();
    };
}

If we want the same level of accuracy as performance.now() in node, we'll need something similar I guess. It's probably overkill though, this might be enough:

function now() {
    if ( typeof (process) !== 'undefined' ) { 
        var time = process.hrtime();

        // Convert [seconds, nanoseconds] to milliseconds.
    return time[0] * 1000 + time[1] / 1000000;
    }

    return ( performance || Date ).now();
}

@Mugen87 I made a pull request #10732. Check please

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fernandojsg picture fernandojsg  路  85Comments

goodsign picture goodsign  路  101Comments

jonobr1 picture jonobr1  路  95Comments

arefin86 picture arefin86  路  64Comments

arctwelve picture arctwelve  路  92Comments