psychopy.org | Reference | Downloads | Github

Incorrect frame rate logs on Pavlovia

Description of the problem: I am running an online study on Pavlovia in which I need to present images for the very short duration of 16.6ms (rounded to 17ms). I also need the refresh rate of the monitor.

The problem is, I have been told that some of the refresh rates that were logged in the .csv output are not the same as the real refresh rate - all the logs say 60Hz but some monitors were in fact e.g. 144Hz. I see in the JS file that:

>  // store frame rate of monitor if we can measure it successfully
>   expInfo['frameRate'] = psychoJS.window.getActualFrameRate();
>   if (typeof expInfo['frameRate'] !== 'undefined')
>     frameDur = 1.0 / Math.round(expInfo['frameRate']);
>   else
>     frameDur = 1.0 / 60.0; // couldn't get a reliable measure so guess

which I presume means that in my data the refresh rate either really is 60Hz, or it could not be measured successfully so it was guessed.

I also looked at the .log files, and calculated logged duration of each image by subtracting the timestamp of ‘autoDraw = False’ from ‘autoDraw = True’ in each trial, and I get a very variable distribution of durations (from 0ms to about 25-30ms), which does not peak at 17ms (but at about 11-13ms). This happens regardless of whether I set the experiment settings to display the image for 1 frame (on confirmed 60Hz monitors) or 17ms. Interestingly, I also present a blank screen for 17ms, and in this case the distribution of the onset-offset differences peaks at 17ms, with much less spread. Other images presented for 2000ms also show peaks at the expected value, and little spread.

My questions would be:
-how could I make sure that the logged refresh rate is correct?
-if I want to clean the data by excluding trials with frame drops/gains, which would be the best approach given the available data and taking into account measurement error?

Any help would be much appreciated!

I don’t think the getActualFrameRate() does anything at all. This is the source code (https://psychopy.github.io/psychojs/core_Window.js.html) of that function. No idea if it is possible to get the refresh rate of screens online.

	/**
	 * Estimate the frame rate.
	 * 
	 * @name module:core.Window#getActualFrameRate
	 * @function
	 * @public
	 * @return {number} always returns 60.0 at the moment
	 * 
	 * @todo estimate the actual frame rate.
	 */
	getActualFrameRate()
	{
		// TODO
		return 60.0;
	}
1 Like

ah okay, I see, that makes sense… Thank you very much for pointing this out!

I see online some suggestions of using the JS function requestAnimationFrame, however I am not sure how/if it is possible to integrate it with PsychoJS…

I have tried (unsuccessfully) to use a separate JS function called requestAnimationFrame, as described here: https://stackoverflow.com/questions/6131051/is-it-possible-to-find-out-what-is-the-monitor-frame-rate-in-javascript/44013686

This code below returns a value, but it’s clearly incorrect - I don’t really know JS but I am posting it below in case anyone can spot any errors:

if (!window.requestAnimationFrame) {
    window.requestAnimationFrame =
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame;
}

var t = [];
function animate(now) {
    
    t.unshift(now);
    if (t.length > 10) {
        var t0 = t.pop();
        var fps = Math.floor(1000 * 10 / (now - t0));
    }
    window.requestAnimationFrame(animate);
};
window.requestAnimationFrame(animate);
var frRate  = window.requestAnimationFrame(animate); 
expInfo['realFrameRate'] = frRate;

Try averaging them out over, say 60 frames: record time at first one, and at 61th one, then dive divide time difference by 60. Also, after you’re done with your measurement, be sure to stop calling rAF again, so it doesn’t get in the way of your actual experiment.

Hello, thank you very much for the suggestions! The value that I get in the logs are three digits (for my 60Hz monitor, it logged on three separate occasions 869, 737, and 1232), I am not sure at all why.
I call this in the beginning of a routine in the instructions part of the experiment, and everything afterwards looks exactly as before, so I would guess the call stops at the end of the routine.

I made a little demo for you. Clone and run this experiment:
https://gitlab.pavlovia.org/tpronk/demo_refresh_rate

On my 143 Hz screen (~7 ms per refresh) I get a duration per frame of indeed 7 ms.

This works perfectly, thank you very much for the help!
I tweaked it a little bit to change how the values are logged, I’ll post below what I ended up adding to my Instructions routine (which is mostly your code), in case anyone else needs it - the difference here I guess is that the interval for averaging is variable e.g. for as long as the routine is viewed:

Begin Experiment:

window.refreshCount = 0;
window.refreshStart = null;
window.refreshFinish = null;

Each Frame:

    if (window.refreshStart === null) {
        window.refreshStart = window.performance.now();
     } else {
        window.refreshFinish = window.performance.now();
        window.refreshCount++;    
     }

    console.log(window.refreshCount);
    console.log(window.performance.now());

End Routine:

let duration = window.refreshFinish - window.refreshStart;
expInfo['realFrameRate'] = (duration / window.refreshCount);

Happy it works and thanks for contributing your version!