psychopy.org | Reference | Downloads | Github

Getting response time from keyboard component

Hello,

I am building an experiment that uses a keyboard component with the Builder (where a key press forces the end of the routine).
How can I access/calculate the response time for a key press in a code component?

Or is this not possible and I have to set up the keyboard in a code component (which would allow me to use getKeys)

Thanks in advance, Guido

Hi @Guido_Biele, there is an example here in the online documentation :

https://www.psychopy.org/api/hardware/keyboard.html

Thanks @dvbridges ,

I tried this, but maybe I am not fully understanding this.

I have set up a keyboard with the name key_resp in the Builder.
Now I am trying to access response times with the following lines at the end of the routine in which key_resp is defined:

keys = key_resp.getKeys()
for thisKey in keys:
    rt = thisKey.rt

But the variable rt stays empty.

What am I doing wrong here?

OK,
I made it more complicated than it is.

To access response time(s) of a keyboard component named for example my_keyboard, one can simply use rt = my_keyboard.rt.

In perhaps a related vein, I was wondering how to retrieve the response time outside of the trial’s while loop. For example,

from psychopy.hardware import keyboard
from psychopy import core, clock, visual

win = visual.window(fullscr = False)

kb = keyboard.Keyboard()
trialClock = core.Clock()

for i in range (4):
    # trial begins
    kb.clock.reset()  # timer (re)starts
    trialClock.reset()

    while trialClock.getTime() <  5:
        kb.getKeys(['right', 'left'])
        win.flip()

    print (kb.rt) # yields an empty list; i'd like it to print all the RT's

win.close()
core.quit()

You need to store the result(s) returned by this function:

kb.getKeys(['right', 'left'])

At the moment, you’re checking for a response but the answer immediately disappears into the ether because you don’t assign it to a name.

You’re right. Perhaps that wasn’t the best reprex. Even when I do store the results, though, it’s the same issue. The solution would seem to be calling a .getTime() function, if a key has been pressed, but I’m ultimately hoping to extract an RT that is checked more often than once per frame. The best I can do at the moment is checking once per frame, as in the following:

from psychopy.hardware import keyboard
from psychopy import core, clock, visual

win = visual.window(fullscr = False)

kb = keyboard.Keyboard()
trialClock = core.Clock()

for i in range (4):
    # trial begins
    kb.clock.reset()  # timer (re)starts
    trialClock.reset()

    while trialClock.getTime() <  5:
        if (len(kb.getKeys(['right', 'left'])) > 0):
            t = kb.clock.getTime()
        win.flip()

    print (t) #would print the most recent RT, which is fine in my case, but that would be rounded to the frame rate

win.close()
core.quit()

I’ve read through most if not all the posts under these search terms but I’m still struggling to find an answer to this frame-rate/RT question.

With the new Keyboard class, you don’t need to worry about how often you check for keypresses. Keyboard returns Keypress objects that are tagged with the time at which they occurred, not the time when you checked for the response, so we don’t need to worry about how frequently we check (unlike the old event module, which was limited in its resolution to how frequently you were polling it).

i.e. The key thing to realise is that these Keypress objects have their own .rt attribute, so can be queried directly, as shown in this example:

https://www.psychopy.org/api/hardware/keyboard.html#psychopy.hardware.keyboard.KeyPress

So you don’t need to ask the keyboard object itself for its time value (which will probably no longer reflect the time when the key was pushed).

oh how phenomenal- if you don’t mind me asking, where might the keys = kb.getKeys() go in my reprex? On the one hand I’d imagine it should go inside the while loop, but on the other hand I’m not sure how I could then call the keys variable (or perhaps [print (akey.rt) for akey in keys]) outside the while loop, such as where I currently have print (t). Either way thanks so much

There are a couple of approaches, but bear in mind that I’m not super-familiar with the new Keyboard class.

If you don’t actually need to respond to the keys in real time, then I think you could just have an arrangement like this:

# draw stimuli for 5 seconds
while trialClock.getTime() <  5:
    stim.draw()
    win.flip()

keys = kb.getKeys()

for key in keys:
    print(key.rt)

I think this would return to you all of the keys that were pressed since the keyboard was reset, with their associated reaction times. i.e. you don’t need to be checking for them in real-time, unless you want to respond to them in real-time (e.g. to stop the trial immediately when a key is pushed).

Otherwise, a second approach would be to create an empty list and then append any new responses to it:

keys = []

# draw stimuli for 5 seconds
while trialClock.getTime() <  5:
    stim.draw()
    keys = keys.append(kb.getKeys())
    win.flip()

Maintaining a list in real-time would allow you to, say, finish the trial when five keypresses have been detected.

I guess it depends what you want to do with the responses, and when.

thanks so much! that led me to settle on the following code

from psychopy.hardware import keyboard
from psychopy import core, clock, visual

win = visual.Window(fullscr = False)

kb = keyboard.Keyboard()
trialClock = core.Clock()

for i in range (4):
    # trial begins
    kb.clock.reset()  # timer (re)starts
    trialClock.reset()

    while trialClock.getTime() <  5:
        keys = kb.getKeys()
        if len(keys) > 0:
            print (keys[0].rt)
            break # skips to the next trial upon button press
        win.flip()

win.close()
core.quit()

I also tested this vs event.getKeys(), and sure enough the RT is more precise for the keyboard.Keyboard . One thing I noticed, though, is the len(keys) > 0 gets picked up about 1 ms after button press; it’s as if the system accurately records the RT but takes .1 seconds to store that value. Not positive why that is happening, but it’s small potatoes as long as the RT is accurate.

Your time values there seem a bit inconsistent (0.1 ms vs 0.1 seconds), but there can be a few things going on. Firstly, the while loop speed is governed by a the win.flip() call. This pauses the code until the next screen refresh, so the loop would run at 16.7 ms per iteration on a typical 60 Hz display. This limits the time resolution of event.getKeys(), which measures the time at which it is called, but not Keyboard.getKeys(), because the Keyboard object is checking for responses in a process that is independent of the rest of PsychoPy, and so isn’t affected by the structure of your code.

Lastly, you should avoid print() calls in operational PsychoPy code - they really should just be used when debugging a script. When there are a lot of them, going out to the standard Python output like this can lead to slow downs (although that isn’t likely too much of an issues in this particular example).

I actually made a typo when I said .1 ms, I meant .1 seconds and I just edited my response accordingly- thanks for the catch! And good points about the print function. Indeed I’m printing only for the reprex and in my code I’m storing the value into a csv cell, so I’m content with my reprex in my above response. Anyways, thanks again for all your help!