URL of experiment: training_paradigm_s1 [PsychoPy]
Description of the problem:
Hello All,
Any help would be greatly appreciated. I am in the process of trying to get my experiment online. Whilst checking if blocks run properly, I noticed that trials on a certain routine sporadically cause an “Unspecified Javascript Error.” After lots of console.log’ging, I narrowed the problem down to a Scheduler.Event.FLIP_REPEAT call on the first flip of the eachFrame function for that routine on the trial that causes the crash (which appears to be random). Why that occasionally causes a problem, I am at a loss. I have the experiment set up so that if you mash spacebar it will take you through the instructions to the correct block, if you go through enough trials (press ‘1’, ‘2’, or ‘3’ randomly), you will eventually get the error too. I am sure that I am at fault here. The experiment is a builder port from a project with a lot of hacky python code components. Please excuse any strange commit messages or comments in the code, it has been a frustrating few days. Please let me know if I am missing any information. Thank you for your time!
Do you have a flip command in each frame?
Flip should not be used in Builder experiments. There is an automatic flip at the end of each frame.
Sorry, I should have been more clear. As you know, when the builder exports an experiment to JavaScript, there is an eachFrame function for each routine. For the routine that is causing the error (on the seemingly random trials where the error occurs), the error occurs immediately following the first flip call of the eachFrame function (i.e. after the end of the first pass of the eachFrame function). This leads me to suspect that something is happening during that function sometimes which causes the screen to crash when it is flipped. I am out of ideas as to what though.
In that case, please could you show the contents of your Each Frame code (Python + JS) where it’s crashing?
I certainly can.
Here is the python code:
# -------Run Routine "criterion_trial"-------
while continueRoutine:
# get current time
t = criterion_trialClock.getTime()
tThisFlip = win.getFutureFlipTime(clock=criterion_trialClock)
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_1_display* updates
if image_1_display.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
image_1_display.frameNStart = frameN # exact frame index
image_1_display.tStart = t # local t and not account for scr refresh
image_1_display.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(image_1_display, 'tStartRefresh') # time at next scr refresh
image_1_display.setAutoDraw(True)
# *label_1* updates
if label_1.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
label_1.frameNStart = frameN # exact frame index
label_1.tStart = t # local t and not account for scr refresh
label_1.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(label_1, 'tStartRefresh') # time at next scr refresh
label_1.setAutoDraw(True)
# *image_2_display* updates
if image_2_display.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
image_2_display.frameNStart = frameN # exact frame index
image_2_display.tStart = t # local t and not account for scr refresh
image_2_display.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(image_2_display, 'tStartRefresh') # time at next scr refresh
image_2_display.setAutoDraw(True)
# *label_2* updates
if label_2.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
label_2.frameNStart = frameN # exact frame index
label_2.tStart = t # local t and not account for scr refresh
label_2.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(label_2, 'tStartRefresh') # time at next scr refresh
label_2.setAutoDraw(True)
# *image_3_display* updates
if image_3_display.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
image_3_display.frameNStart = frameN # exact frame index
image_3_display.tStart = t # local t and not account for scr refresh
image_3_display.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(image_3_display, 'tStartRefresh') # time at next scr refresh
image_3_display.setAutoDraw(True)
# *label_3* updates
if label_3.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
label_3.frameNStart = frameN # exact frame index
label_3.tStart = t # local t and not account for scr refresh
label_3.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(label_3, 'tStartRefresh') # time at next scr refresh
label_3.setAutoDraw(True)
# *features_text* updates
if features_text.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
features_text.frameNStart = frameN # exact frame index
features_text.tStart = t # local t and not account for scr refresh
features_text.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(features_text, 'tStartRefresh') # time at next scr refresh
features_text.setAutoDraw(True)
# *crit_key_resp* updates
waitOnFlip = False
if crit_key_resp.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
crit_key_resp.frameNStart = frameN # exact frame index
crit_key_resp.tStart = t # local t and not account for scr refresh
crit_key_resp.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(crit_key_resp, 'tStartRefresh') # time at next scr refresh
crit_key_resp.status = STARTED
# keyboard checking is just starting
waitOnFlip = True
win.callOnFlip(crit_key_resp.clock.reset) # t=0 on next screen flip
win.callOnFlip(crit_key_resp.clearEvents, eventType='keyboard') # clear events on next screen flip
if crit_key_resp.status == STARTED and not waitOnFlip:
theseKeys = crit_key_resp.getKeys(keyList=['1', '2', '3'], waitRelease=False)
_crit_key_resp_allKeys.extend(theseKeys)
if len(_crit_key_resp_allKeys):
crit_key_resp.keys = _crit_key_resp_allKeys[-1].name # just the last key pressed
crit_key_resp.rt = _crit_key_resp_allKeys[-1].rt
# was this correct?
if (crit_key_resp.keys == str(new_corr_ans)) or (crit_key_resp.keys == new_corr_ans):
crit_key_resp.corr = 1
else:
crit_key_resp.corr = 0
# a response ends the routine
continueRoutine = False
# check if all components have finished
if not continueRoutine: # a component has requested a forced-end of Routine
break
continueRoutine = False # will revert to True if at least one component still running
for thisComponent in criterion_trialComponents:
if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
continueRoutine = True
break # at least one component has not yet finished
# refresh the screen
if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
win.flip()
And here is the corresponding JavaScript code:
function criterion_trialRoutineEachFrame(snapshot) {
return function () {
console.log("begin criterion_trial Each frame")
//------Loop for each frame of Routine 'criterion_trial'-------
let continueRoutine = true; //until were told otherwise
// skip routine if we have reached the required criterion
if (criterion_reached == true) {
console.log("criterion reached, skipping trial")
return Scheduler.Event.NEXT;
}
// get current time
t = criterion_trialClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
// *image_1_display* updates
if (t >= 0.5 && image_1_display.status === PsychoJS.Status.NOT_STARTED) {
console.log("initialising image 1")
// keep track of start time/frame for later
image_1_display.tStart = t; // (not accounting for frame time here)
image_1_display.frameNStart = frameN; // exact frame index
console.log(image_2_display.image);
image_1_display.setAutoDraw(true);
console.log("done")
}
// *label_1* updates
if (t >= 0.0 && label_1.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
label_1.tStart = t; // (not accounting for frame time here)
label_1.frameNStart = frameN; // exact frame index
label_1.setAutoDraw(true);
}
// *image_2_display* updates
if (t >= 0.5 && image_2_display.status === PsychoJS.Status.NOT_STARTED) {
console.log("initialising image 2")
// keep track of start time/frame for later
image_2_display.tStart = t; // (not accounting for frame time here)
image_2_display.frameNStart = frameN; // exact frame index
console.log(image_2_display.image);
image_2_display.setAutoDraw(true);
console.log("done")
}
// *label_2* updates
if (t >= 0.0 && label_2.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
label_2.tStart = t; // (not accounting for frame time here)
label_2.frameNStart = frameN; // exact frame index
label_2.setAutoDraw(true);
}
// *image_3_display* updates
if (t >= 0.5 && image_3_display.status === PsychoJS.Status.NOT_STARTED) {
console.log("initialising image 3")
// keep track of start time/frame for later
image_3_display.tStart = t; // (not accounting for frame time here)
image_3_display.frameNStart = frameN; // exact frame index
console.log(image_2_display.image)
image_3_display.setAutoDraw(true);
console.log("done")
}
// *label_3* updates
if (t >= 0.0 && label_3.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
label_3.tStart = t; // (not accounting for frame time here)
label_3.frameNStart = frameN; // exact frame index
label_3.setAutoDraw(true);
}
// *features_text* updates
if (t >= 0.5 && features_text.status === PsychoJS.Status.NOT_STARTED) {
console.log("intialising features text")
// keep track of start time/frame for later
features_text.tStart = t; // (not accounting for frame time here)
features_text.frameNStart = frameN; // exact frame index
features_text.setAutoDraw(true);
console.log("done")
}
// *crit_key_resp* updates
if (t >= 0.5 && crit_key_resp.status === PsychoJS.Status.NOT_STARTED) {
console.log("initialising key_resp")
// keep track of start time/frame for later
crit_key_resp.tStart = t; // (not accounting for frame time here)
crit_key_resp.frameNStart = frameN; // exact frame index
// keyboard checking is just starting
psychoJS.window.callOnFlip(function() { crit_key_resp.clock.reset(); }); // t=0 on next screen flip
psychoJS.window.callOnFlip(function() { crit_key_resp.start(); }); // start on screen flip
psychoJS.window.callOnFlip(function() { crit_key_resp.clearEvents(); });
console.log("done")
}
console.log(crit_key_resp.status)
if (crit_key_resp.status === PsychoJS.Status.STARTED) {
console.log("begin tracking key_resp")
let theseKeys = crit_key_resp.getKeys({keyList: ['1', '2', '3'], waitRelease: false});
_crit_key_resp_allKeys = _crit_key_resp_allKeys.concat(theseKeys);
if (_crit_key_resp_allKeys.length > 0) {
crit_key_resp.keys = _crit_key_resp_allKeys[_crit_key_resp_allKeys.length - 1].name; // just the last key pressed
crit_key_resp.rt = _crit_key_resp_allKeys[_crit_key_resp_allKeys.length - 1].rt;
// was this correct?
if (crit_key_resp.keys == new_corr_ans) {
crit_key_resp.corr = 1;
} else {
crit_key_resp.corr = 0;
}
// a response ends the routine
continueRoutine = false;
}
console.log("end tracking key_resp")
}
console.log("checking if experiment should be escaped")
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}
console.log("checking if routine should terminate should be escaped")
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
console.log("check if the components are still running")
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of criterion_trialComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
console.log("refresh screen or not")
// refresh the screen if continuing
if (continueRoutine) {
console.log("refresh screen")
return Scheduler.Event.FLIP_REPEAT;
} else {
console.log("move to next routine")
return Scheduler.Event.NEXT;
}
};
}
Note that my assumptions about the root of the problem are based on the fact that, in the console, I see the message “refresh screen” immediately prior to the error.
Are you using Builder?
If so, please could you show the contents of your Each Frame code component (Python + JS)? The code you’ve shown contains lots of code generated by PsychoPy and I’m wanting to see your custom code.
Ah yes, sorry.
I do not have any Each Frame code components for this Routine. There is one for Begin Routine.
Here is the Python Code:
images = [[1,image_1_display], [2, image_2_display], [3, image_3_display]]
np.random.shuffle(images)
for i in range(len(images)):
images[i][1].pos = (image_x_locations[i], 0)
if images[i][0] == corr_ans:
new_corr_ans = i+1
criterion_features = [feature_1, feature_2, feature_3]
np.random.shuffle(criterion_features)
features_text.setText(f"Which is {criterion_features[0]}, {criterion_features[1]}, and {criterion_features[2]}")
NB: image_<1-3>_display
are image components, and image_x_locations
is an array of length: 3, populated with integers representing locations on the screen, feature_<1-3>
are parameters from a conditions csv file.
Because the code translator cannot automatically translate this code to JS (it doesn’t seem to recognise numpy as Python only), I had to make a few changes. I used a custom function to shuffle the images array. Therefore, the JS equivalent (with the function I used) is:
// function to shuffle array
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// code snippet from beginRoutine
// update component parameters for each repeat
images = [[1, image_1_display], [2, image_2_display], [3, image_3_display]];
shuffleArray(images);
for (var i = 0, _pj_a = images.length; (i < _pj_a); i += 1) {
images[i][1].pos = [image_x_locations[i], 0];
if ((images[i][0] === corr_ans)) {
new_corr_ans = (i + 1);
}
}
criterion_features = [feature_1, feature_2, feature_3];
shuffleArray(criterion_features);
features_text.setText(`Which is ${criterion_features[0]}, ${criterion_features[1]}, and ${criterion_features[2]}`);
Thank you!
This way of concatenating strings doesn’t work in JS.
[quote=“JDVinson, post:7, topic:20895”]
features_text.setText(`Which is ${criterion_features[0]}, ${criterion_features[1]}, and ${criterion_features[2]}`);
This must work because not all trials lead to the error. Are there certain conditions under which this would not work?