Inaccurate stimulus duration timing for online image components

If this template helps then use it. If not then just delete and start from scratch.

OS (e.g. Win10): MacOS Sequoia
PsychoPy version (e.g. 2024.2.4 Py 3.8): V2024.2.4.
Standard Standalone Installation? (y)
URL of experiment:
Do you want it to also run locally? (N)
What are you trying to achieve?:

My stimulus is always being presented for a frame shorter than they should be.

I am unsure if this is due to inaccuracies in recording the onset and offset time of my stimulus (4 Image Components and 1 polygon component). For all image components, in the data tab I have the “Save onset/offset times” box selected, but the onset and offset times are not automatically recorded.

What did you try to make it work?:

I was able to record all the onset times using:

thisExp.addData(‘Image1_onset’, Image1.tStart)

To record offset times, when autoDraw is turned to null, I record the time when this occurs (see code below). Also, as an auxiliary method to measure stimulus duration, I record the number of frames where autoDraw = True.

# Begin Routine tab
mo_numFrame = 0
MO_off = []
store_MO_Off = False
# Each frame tab
if mask_one.autoDraw:
    mo_numFrame = mo_numFrame + 1
    store_MO_Off = True
else:
    if store_MO_Off:
        if len(MO_off) == 0:
            MO_off.append(t)
            store_MO_Off = False

However, both the number of frames recorded and the offset time, suggest that stimulus are being presented for 1 frame too short. For example, when I request 100ms, at a frame rate of 60hz, the stimulus are only on the screen for 5 frames. If I look at the time when autoDraw is no longer True, it shows the same pattern, with autoDraw being turned to Null after approximately 83ms. Inspection of log files confirmed this pattern.

I have a mask presented 100ms after image onset. Interestingly, if I compare the Image’s onset time to the onset time of the Mask, they are exactly 100ms apart, on almost all trials. Suggesting, the image is being displayed for 100ms.

This brings me to my questions, 1) is my stimulus timing off**, 2)** what is the optimal and most accurate for measuring stimulus duration and stimulus offset online?

Thanks in advance for any assistance any one can provide! :slight_smile:

Dear Tim

The best way to actually measure if you are actually losing frames is a photodiode.

As for where your singular frame might be going try these posts? Need help choosing frames or timed duration for my visual stimulus - #3 by wakecarter

Your lost frame might be in the buffer frame at trial start

    # reset timers
    t = 0
    _timeToFirstFrame = win.getFutureFlipTime(clock="now")
    frameN = -1
    
    # --- Run Routine "trial" ---
    trial.forceEnded = routineForceEnded = not continueRoutine
    while continueRoutine and routineTimer.getTime() < 1.0:
        # get current time
        t = routineTimer.getTime()
        tThisFlip = win.getFutureFlipTime(clock=routineTimer)
        tThisFlipGlobal = win.getFutureFlipTime(clock=None)
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        
        # *image* updates
        
        # if image is starting this frame...
        if image.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            image.frameNStart = frameN  # exact frame index
            image.tStart = t  # local t and not account for scr refresh
            image.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(image, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.timestampOnFlip(win, 'image.started')
            # update status
            image.status = STARTED
            image.setAutoDraw(True)
        
        # if image is active this frame...
        if image.status == STARTED:
            # update params
            pass
        
        # if image is stopping this frame...
        if image.status == STARTED:
            # is it time to stop? (based on global clock, using actual start)
            if tThisFlipGlobal > image.tStartRefresh + 1.0-frameTolerance:
                # keep track of stop time/frame for later
                image.tStop = t  # not accounting for scr refresh
                image.tStopRefresh = tThisFlipGlobal  # on global time
                image.frameNStop = frameN  # exact frame index
                # add timestamp to datafile
                thisExp.timestampOnFlip(win, 'image.stopped')
                # update status
                image.status = FINISHED
                image.setAutoDraw(False)

I may be incorrect here but the

frameN = frameN + 1

is creating a buffer frame on frame 0, then starts drawing objects on frame 1. Hence a “lost” frame.

Issac

Hi Isaac,

Thank you so much for your detailed reply! Regarding the buffer frame - that makes a lot of sense, I suspect that is what is causing our “inaccurate” frame recordings.

A rough solution we came up with, is we added a polygon stimulus with opacity = 0, and a position of (0.5, 0.5), that appears on the screen at the time of stimulus offset for our stimulus of interest.

When we compare the onset time for this polygon stimulus (.tStart) to the onset time of our stimulus of interest, the duration times we extract suggest that our stimulus of interest is on the screen for approximately the duration we want.

I am not sure how accurate this is, but it at least seems to suggest that there is not a timing issue in our online study?