psychopy.org | Reference | Downloads | Github

Abnormal duration of first frame when displaying visual stimuli (text, element array)

Hi,

Motivated by the results of the Timing study, I just ran the code below to check the stability of my frame durations for a textStim versus an ElementArrayStim stimulus. The framerate of my monitor is 60 Hz, graphic card is a NVidia GTX, and os is windows 10. Note that the timeByFrames demo returns stable frame durations (.01666sc with a very small SD).

from psychopy import core, data, event, visual, gui
import numpy as np
import os
from pathlib import Path
from psychopy.hardware import keyboard
from numpy.random import shuffle, randint

win = visual.Window(fullscr=True, 
    monitor='mathieu', color=[145,145,145], colorSpace='rgb255', units='cm')
police = 'Consolas' #monospaced font
win.setMouseVisible(False) #hide the mouse cursor!!!

size_instruc = 0.5
instructions = visual.TextStim(win=win, 
    ori=0.0,#orientation
        text =u"Vous allez réaliser une tâche de perception des objets dans l’espace.\
 Cette tâche permet de mesurer la capacité de logique mathématique des participants.\
 L'objectif de cette Ă©tude est de comparer la performance des femmes et des hommes.\n\n\
Veuillez d'abord indiquer votre genre: 'Femme', 'Homme', 'Autre' sur la feuille à côté de vous, puis pressez la barre d'espace. ", 
    font=police,
    pos=[0, 0],
    height=size_instruc,
    color=[0,0,0],
    colorSpace='rgb255',
    wrapWidth = 15)      

#draw 50 points at random locations on a 10x10 grid
N = 100 
elemSize = 0.2
# Generate positions for the 10 x 10 grid
positions = []
for i in np.linspace(-4, 4, 10):
        for j in np.linspace(-4, 4, 10):
            positions.append([i, j])
        
# Generate element array stim
globForm = visual.ElementArrayStim(win,
    nElements=N, sizes=elemSize, sfs=0,
    elementTex=None,
    elementMask="circle",
    xys=positions, colors=[145, 145, 145], colorSpace='rgb255', units='cm')

cols = []
displayN =  50  # Number of visible elements
cols = [0] * displayN + ([145] * (100-displayN))
cols = [[i,i,i] for i in cols]
shuffle(cols)
globForm.colors = cols

# initialize keyboard
resp = keyboard.Keyboard()
defaultKeyboard = keyboard.Keyboard()#for escaping only

kb_delay = 0
frame = 0
win.callOnFlip(resp.clearEvents, eventType='keyboard')
win.callOnFlip(resp.clock.reset)
print ("duration of first 10 frames, instructions stimulus (textStim)")
while True:  
    if kb_delay==1:#check keyboard buffer AFTER first draw of stimulus and clock reset
        theseKeys = resp.getKeys(keyList=['space'], waitRelease=False)
        if len(theseKeys):
            break
        if defaultKeyboard.getKeys(keyList=["escape"], waitRelease=False):
            core.quit()    
    instructions.draw()
    win.flip()
    if frame < 10:
        print (resp.clock.getTime())
    if kb_delay ==0:
        kb_delay =1
    frame = frame + 1

win.flip()
print ("")
print ("duration of first 10 frames, globForm stimulus (ElementArrayStim)")
kb_delay = 0
frame = 0
win.callOnFlip(resp.clearEvents, eventType='keyboard')
win.callOnFlip(resp.clock.reset)
while True:  
    if kb_delay==1:#check keyboard buffer AFTER first draw of stimulus and clock reset
        theseKeys = resp.getKeys(keyList=['space'], waitRelease=False)
        if len(theseKeys):
            break
        if defaultKeyboard.getKeys(keyList=["escape"], waitRelease=False):
            core.quit()    
    globForm.draw()
    win.flip()
    if frame < 10:
        print (resp.clock.getTime())
    if kb_delay ==0:
        kb_delay =1
    frame = frame + 1
core.quit()

which returns:

duration of first 10 frames, instructions stimulus (textStim)
0.00011660000018309802
0.009802399999898626
0.017592700001841877
0.028969800001505064
0.045913300000393065
0.06249250000109896
0.07880700000168872
0.09597180000127992
0.11230520000026445
0.12928459999966435

duration of first 10 frames, globForm stimulus (ElementArrayStim)
3.51999988197349e-05
0.014455000000452856
0.031261000000085915
0.04761280000093393
0.0644293999976071
0.08123459999842453
0.09793029999855207
0.1145102999980736
0.13087180000002263
0.1477792000005138

I don’t understand why the first frame is not ~.01666 sc. I tried the above code on different machines running on windows 10 with different graphic cards (NVidia GTX, Radeon, Intel), and got very similar results. Could please explain why the duration of the first frame is smaller than expected? I am a bit worried because this could suggest that the reset of my clock may not be synchronized with my first frame. How can I solve the issue?

Hi,

Firstly I’d suggest not printing to Python’s standard output in a tight loops like this: probably better to log the results and print them out in one go at the end. You could also be logging the exact time of the win.flip(), rather than the time fraction of a second later.

Secondly, I’m not sure I entirely understand everything here, but would suggest the problem is not having a defined point to start the timing from. i.e. when your code starts, it is not in sync with the frame drawing loop. i.e. you create a keyboard and zero its timer, but that could be happening at any point within the current screen refresh cycle, and will differ randomly from run to run. You then call win.flip() and simultaneously reset the clock to zero again. So the very first interval will always be less than 16.7 ms, and on average would be about 8.3 ms (unless the intervening code between the keyboard instantiation and the win.flip() takes more than 16.7 ms to run).

So to get constant timing throughout your while loop, you really need to insert a win.flip() immediately before the loop starts. That way, the very first interval should be exactly 16.7 ms, as you have a defined point (the previous frame being drawn) to start the loop from.

1 Like

@Michael @jon
Sorry, I wasn’t clear in my previous post. Please consider this short piece of code that should better illustrate the issue:

from psychopy import core, data, event, visual, gui
import numpy as np
import os
from psychopy.hardware import keyboard
np.set_printoptions(4, suppress=True)

win = visual.Window(fullscr=True, 
    monitor='mathieu', color=[145,145,145], colorSpace='rgb255', units='cm')
    
police = 'Consolas' 
win.setMouseVisible(False) 

# initialize keyboard
resp = keyboard.Keyboard()

some_text = visual.TextStim(win=win, 
    text ="Hello world", 
    font=police,
    pos=[0, 0],
    height=0.5,
    color=[0,0,0],
    colorSpace='rgb255')      


frame = 0
temp = np.zeros(10)
win.callOnFlip(resp.clock.reset)
while True:  
    theseKeys = resp.getKeys(keyList=['space'], waitRelease=False)
    if len(theseKeys):
        break
    some_text.draw() 
    win.flip()
    t = resp.clock.getTime()
    if frame < 10:
        temp[frame] = t
    frame = frame + 1

print (temp)
core.quit()

Please note that the call to win.callOnFlip(resp.clock.reset) occurs just before the while loop, so the timer should start simulatenously with the first win.flip(). Consequently, the first value in the “temp” array should be close to 0. The following values should be close to .01667. I just ran this code two times and got the following values:

[0.0001 0.06 0.0663 0.0813 0.098 0.1146 0.1313 0.1481 0.1647 0.1813]
[0. 0.0039 0.0117 0.0286 0.0451 0.0619 0.0785 0.0947 0.1115 0.1283]

which doesn’t make sense to me. That’s why I would appreciate some clarifications on what’s going on here.
Thanks!