Task reaction time not recording for select trials

Hi there, I am running an fMRI task in Psychopy 2025.X.X on Windows 11 in which a target appears and the participant is instructed to press the button during this target. The task is supposed to record the reaction time for this button press. For the most part, the code does this, however, in a variable number of trials, the reaction time is not recorded. The log files all look like this when this occurs:

319.4994 EXP unnamed Polygon: autoDraw = True
319.4994 EXP unnamed Polygon: autoDraw = True
319.5161 EXP unnamed Polygon: autoDraw = True
319.5328 EXP unnamed Polygon: autoDraw = True
319.5495 EXP unnamed Polygon: autoDraw = True
319.5661 EXP unnamed Polygon: autoDraw = True
319.5825 EXP unnamed Polygon: autoDraw = True
319.5993 EXP unnamed Polygon: autoDraw = True
319.6161 EXP unnamed Polygon: autoDraw = True
319.6329 EXP unnamed Polygon: autoDraw = True
319.6495 EXP unnamed Polygon: autoDraw = True
319.6662 EXP unnamed Polygon: autoDraw = True
319.6828 EXP unnamed Polygon: autoDraw = True
319.6995 EXP unnamed Polygon: autoDraw = True
319.7161 EXP unnamed Polygon: autoDraw = True
319.7328 EXP unnamed Polygon: autoDraw = True
319.7343 DATA Keypress: 1
319.7495 EXP unnamed Polygon: autoDraw = False

I tried switching between Psychopy default dependencies and iohub to accept button presses and the issue persists. I am wondering if anyone has experienced similar issues and if there are any easy workarounds? We are using a Fiber Optic Response Devices 8 Button fMRI Response Pad.

Thank you!

Akash

Dear Akash,

Each row of that output is a specific frame. So to me, it looks like your participant pressed “Key 1” at 319.7343 seconds (global clock).

Could you provide more information about your experiment? Are you using builder or coder? If you are using builder, what type of response are you using (keyboard, buttonbox, custom code, serial/parallel)?

Make sure you have the “save onset/offset times” box check as well.

Issac

Hi Isaac,

Thanks for the quick response!

We use the coder for the experiment and are running it separately with both a button box and keyboard inputs. I have found I encounter the same issue in a portion of the trials using both the keyboard and button box.

Does this seem like an issue with the logic of the code? I have tried a number of code changes to account for instances when the input comes in this specific frame with little success.

Thanks!

Akash

Dear Akash,

Could you share some of that code? How much does it differ from psychopy’s basic key press evaluation logic?

    # if key_resp is starting this frame...
    if key_resp.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        key_resp.frameNStart = frameN  # exact frame index
        key_resp.tStart = t  # local t and not account for scr refresh
        key_resp.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(key_resp, 'tStartRefresh')  # time at next scr refresh
        # add timestamp to datafile
        thisExp.timestampOnFlip(win, 'key_resp.started')
        # update status
        key_resp.status = STARTED
        # keyboard checking is just starting
        waitOnFlip = True
        win.callOnFlip(key_resp.clock.reset)  # t=0 on next screen flip
        win.callOnFlip(key_resp.clearEvents, eventType='keyboard')  # clear events on next screen flip
    if key_resp.status == STARTED and not waitOnFlip:
        theseKeys = key_resp.getKeys(keyList=['y','n','left','right','space'], ignoreKeys=["escape"], waitRelease=False)
        _key_resp_allKeys.extend(theseKeys)
        if len(_key_resp_allKeys):
            key_resp.keys = _key_resp_allKeys[-1].name  # just the last key pressed
            key_resp.rt = _key_resp_allKeys[-1].rt
            key_resp.duration = _key_resp_allKeys[-1].duration
            # a response ends the routine
            continueRoutine = False

Also, have you tried between all 3 of the keyboard backends (iohub, pyglet, PsychToolbox)? You only mentioned two in your original post. I know I had issues with that when using a CRS Eyetracker, a Cedrus button box, and a keyboard. I decided to just remove the button box (as gaming keyboards are as good, or better than button boxes these days).

However, looking at your input device, could explain what you are using to convert the optical to parallel/serial/USB? It looks like it uses optical signal and then you have an interim box thing that converst that to USB output (almost similar to a optical→TTL converter). First thing I would do is see what happens when you remove the keyboard input component, and only use the button box. This is to narrow down the problem being specific to the interaction between the button box and keyboard, or is it something specific to how psychopy is reading the input from your “905” interface/ “Current Designs” box

Issac

Hi Isaac,

Below is code for each trial:
`# -------Start Routine “Target”-------
# Log target onset time
exp.addData(‘Tgt.OnsetTime’, runClock.getTime())

while continueRoutine and routineTimer.getTime() > 0:
    # Get current time
    t = TargetClock.getTime()
    
    # Selection screen updates
    if t >= 0.0 and Target.status == NOT_STARTED:
        # Keep track of start time/frame for later
        Target.tStart = t
        # Display target
        Target.setAutoDraw(True)
        # Open response options
        target_response.tStart = t
        target_response.status = STARTED
        # Keyboard checking is just starting
        win.callOnFlip(target_response.clock.reset)  # t=0 on next screen flip
        event.clearEvents(eventType='keyboard')
        theseKeys = []
        
    if Target.status == STARTED and t <= target_durs.loc[0,trial_type]:
        Target.setAutoDraw(True)
        theseKeys = event.getKeys(keyList=forwardKeys)

        if len(theseKeys) > 0:  
            rt = target_response.clock.getTime()
            target_response.rt = rt
            if trial_response == 0:
                trial_response = 1
    
    # Check if all components have finished
    if not continueRoutine:
        break
    continueRoutine = False
    for thisComponent in TargetComponents:
        if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
            continueRoutine = True
            break  # At least one component has not yet finished
    
    # Draw fixation if we're done, so we don't leave a blank screen for any frames
    if not continueRoutine:
        fix.draw()
    win.flip()`

I tried adapting the task to use iohub, however, it was not compatible with our stimulus presentation computer at the scanner as button presses failed to record. Given this is an fMRI task, it unfortunately won’t be possible to switch over to exclusively using a keyboard.

My feeling is the issue lies in how psychopy is reading keyboard presses because the issue is present in both in-scanner runs and out-of-scanner runs on the laptop.

Below is the code specific to where the issue occurs:

`# Set the fixation after target by accounting for the variable target
# time window
if rt:
    fix_after_target = isi_target_isi_time - fix_after_cue - rt
else:
    fix_after_target = isi_target_isi_time - fix_after_cue - target_durs.loc[0,trial_type]

too_slow_rt = show_fixation(fix_after_target)
if too_slow_rt:
    print('too slow rt: ', too_slow_rt)
    trial_response = 3
    exp.addData('trial.too_slow_rt', too_slow_rt)`

Thanks!

Akash