psychopy.org | Reference | Downloads | Github

Checking routine times in psychopyjs

URL of experiment: https://pavlovia.org/run/roberto.petrosino/maskedpriming/html/

I am trying to implement what @Michael and I have discussed here. Basically, I would like to add information about (a) the number of frames drawn for each routine and (b) the actual duration (in seconds) of that routine.


Background. The experiment is a typical visual masked priming experiment, with a forward mask (fmaskWarm routine), a prime stimulus (primeWarm routine) and a target stimulus (targetWarm routine). The forward mask is set to last 500 ms, the prime 33 ms.

Here’s what I did (but click here for the full script). The lines of coded added are followed by an arrow <---:

var frameRemains;
function fmaskWarmRoutineEachFrame() {
...  
  // *text_fmaskWarm* updates
  if (t >= 0.0 && text_fmaskWarm.status === PsychoJS.Status.NOT_STARTED) {
    // keep track of start time/frame for later
    text_fmaskWarm.tStart = t;  // (not accounting for frame time here)
    text_fmaskWarm.frameNStart = frameN;  // exact frame index
    text_fmaskWarm.setAutoDraw(true);
    psychoJS.window.callOnFlip( function () { fmaskWarmClock.reset(); }); <---
  }

  frameRemains = 0.5  - psychoJS.window.monitorFramePeriod * 0.75;  // most of one frame period left
  if (text_fmaskWarm.status === PsychoJS.Status.STARTED && t >= frameRemains) {
    text_fmaskWarm.setAutoDraw(false);
  }
  // check for quit (typically the Esc key)
  if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
    return psychoJS.quit('The [Escape] key was pressed. Goodbye!', false);
  }  
...
}

...

function fmaskWarmRoutineEnd() {
  //------Ending Routine 'fmaskWarm'-------
  for (const thisComponent of fmaskWarmComponents) {
    if (typeof thisComponent.setAutoDraw === 'function') {
      thisComponent.setAutoDraw(false);
    }
  }
  psychoJS.experiment.addData('fr_fmask', frameN); <---
  return Scheduler.Event.NEXT;
}

...

var fmaskWarmDuration;
function primeWarmRoutineEachFrame() {
  //------Loop for each frame of Routine 'primeWarm'-------
  let continueRoutine = true; // until we're told otherwise
  // get current time
  t = primeWarmClock.getTime();
  frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
  // update/draw components on each frame
  
  // *text_primeWarm* updates
  if (t >= 0.0 && text_primeWarm.status === PsychoJS.Status.NOT_STARTED) {
    // keep track of start time/frame for later
    text_primeWarm.tStart = t;  // (not accounting for frame time here)
    text_primeWarm.frameNStart = frameN;  // exact frame index
    text_primeWarm.setAutoDraw(true);
    psychoJS.window.callOnFlip(function(){primeWarmClock.reset();}); <---
  }

  frameRemains = 0.032  - psychoJS.window.monitorFramePeriod * 0.75;  // most of one frame period left
  if (text_primeWarm.status === PsychoJS.Status.STARTED && t >= frameRemains) {
    text_primeWarm.setAutoDraw(false);
  }
  // check for quit (typically the Esc key)
  if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
    return psychoJS.quit('The [Escape] key was pressed. Goodbye!', false);
  }

// check if the Routine should terminate
  if (!continueRoutine) {  // a component has requested a forced-end of Routine
    return Scheduler.Event.NEXT;
  }
  
  continueRoutine = false;  // reverts to True if at least one component still running
  for (const thisComponent of primeWarmComponents)
    if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
      continueRoutine = true;
      break;
    }
  
  // refresh the screen if continuing
  if (continueRoutine && routineTimer.getTime() > 0) {
    return Scheduler.Event.FLIP_REPEAT;
    if ( frameN == 0 ) {
        fmaskWarmDuration = fmaskWarmClock.getTime(); } <---
  }    
  else {
    return Scheduler.Event.NEXT;
  }
}


function primeWarmRoutineEnd() {
  //------Ending Routine 'primeWarm'-------
  for (const thisComponent of primeWarmComponents) {
    if (typeof thisComponent.setAutoDraw === 'function') {
      thisComponent.setAutoDraw(false);
    }
  }
  psychoJS.experiment.addData('t_fmask', fmaskWarmDuration); <---
  psychoJS.experiment.addData('fr_prime', frameN);
  return Scheduler.Event.NEXT;
}

I think I am close to the solution (as in, no error pops up), but there’s something I am missing because when I look at the csv output file, the duration columns t_fmask is empty for some reason. I feel like that must have something to do with the if-statement in which I defined the variable fmaskWarmDuration, but I can’t figure out what is wrong with it.

Can anybody help? Thanks!

After a bit of research on the behavior of the return statement, I think I figure it out. return ends the function execution, so the if-statement relative to the clock of each routine should precede return:

// refresh the screen if continuing
  if (continueRoutine && routineTimer.getTime() > 0) {
    if ( frameN == 0 ) {
        fmaskWarmDuration = fmaskWarmClock.getTime(); } <---
    return Scheduler.Event.FLIP_REPEAT;
  }    
  else {
    return Scheduler.Event.NEXT;
  }

Would any of the developers be willing to take a look at the script and confirm that is all correct?

Thanks!!