I am using jsPsych to create an experiment and I am struggling to sample from two variables simultaneously. Specifically, in each trial, I would like to present a primeWord
and a targetWord
by randomly sampling each of them from its own variable.
I have looked into several resources—such as sampling without replacement, custom sampling and position indices—but to no avail. I'm a beginner at this, so it's possible that one of these resources was relevant (especially the last one, I think).
Could you please consider my code below? In addition to the parallel sampling, how could I save the same trial index in the data
of both primeWord
and targetWord
?
Thank you so very much for your attention
* I've also posted on the jsPsych GitHub. If a solution is found, I will link to it on other question.
<!DOCTYPE html>
<html>
<head>
<!-- jsPsych plugins -->
<script src="../jspsych.js"></script>
<script src="../plugins/jspsych-html-keyboard-response.js"></script>
<script src="../plugins/jspsych-html-button-response.js"></script>
<!-- CSS -->
<link rel="stylesheet" href="../css/jspsych.css">
<style>
body.jspsych-display-element {
color: #ececec;
background-color: #2b2b2b;
}
#jspsych-html-keyboard-response-stimulus {
font-size: 32px;
}
.fas, .far {
color: #b6b6b6;
}
</style>
</head>
<!-- Beginning of the script containing the experiment -->
<script>
/* Create empty timeline object, which will be sequentially filled in using timeline.push() */
var timeline = [];
var instructions = {
type: 'html-button-response',
stimulus: ["<p>Each screen will show a word in lower case, such as 'target'. Press <b>F</b> if the word is primarily abstract</p>" +
'<p>or <b>J</b> if it is primarily concrete. Each word is presented for up to five seconds.</p>'],
choices: ['Ready to start']
}
/* Add instructions to the timeline */
timeline.push(instructions)
/* Stimuli */
var list_primeWords = [
{ primeWord: 'PRIME 1', position: 'prime' },
{ primeWord: 'PRIME 2', position: 'prime' },
{ primeWord: 'PRIME 3', position: 'prime' },
{ primeWord: 'PRIME 4', position: 'prime' },
{ primeWord: 'PRIME 5', position: 'prime' }
];
var list_targetWords = [
{ targetWord: 'target 1', position: 'target', correct_response: 'abstract' },
{ targetWord: 'target 2', position: 'target', correct_response: 'concrete' },
{ targetWord: 'target 3', position: 'target', correct_response: 'abstract' },
{ targetWord: 'target 4', position: 'target', correct_response: 'concrete' },
{ targetWord: 'target 5', position: 'target', correct_response: 'abstract' }
];
/* Procedure */
/* Fixation cross */
var fixation = {
type: 'html-keyboard-response',
stimulus: '+',
choices: jsPsych.NO_KEYS,
trial_duration: function () {
/* Set fixations with a varying duration to boost participants' attention */
return jsPsych.randomization.sampleWithoutReplacement([300, 400, 450, 500, 550, 600, 700], 1)[0];
},
post_trial_gap: 0,
css_classes: ['stimulus']
};
var primeWord = {
type: 'html-keyboard-response',
stimulus: jsPsych.timelineVariable('primeWord'),
choices: jsPsych.NO_KEYS,
trial_duration: 150,
post_trial_gap: function () {
/* Random interstimulus interval */
return jsPsych.randomization.sampleWithoutReplacement([100, 200, 300, 400, 450, 500, 550, 600, 700, 800, 900, 1000, 1100, 1200], 1)[0];
},
css_classes: ['stimulus'],
/* Computation run at the end of each trial */
on_finish: function (data) {
if (data.key_press !== null) {
var primeWord_keypress = 'pressed';
} else {
var primeWord_keypress = 'unpressed';
}
data.primeWord_keypress = primeWord_keypress;
},
sample: {
type: 'without-replacement',
size: 4
}
};
var targetWord = {
type: 'html-keyboard-response',
stimulus: jsPsych.timelineVariable('targetWord'),
choices: ['f', 'j'],
trial_duration: 3000,
post_trial_gap: 0,
css_classes: ['stimulus'],
data: {
correct_response: jsPsych.timelineVariable('correct_response')
},
/* Computation run at the end of each trial */
on_finish: function (data) {
if (data.key_press !== null) {
/* Label correct responses */
if (data.correct_response == 'abstract' && data.key_press == jsPsych.pluginAPI.convertKeyCharacterToKeyCode('f') ||
data.correct_response == 'concrete' && data.key_press == jsPsych.pluginAPI.convertKeyCharacterToKeyCode('j')) {
var accuracy = 'correct';
/* Label incorrect responses */
} else if (data.correct_response == 'abstract' && data.key_press == jsPsych.pluginAPI.convertKeyCharacterToKeyCode('j') ||
data.correct_response == 'concrete' && data.key_press == jsPsych.pluginAPI.convertKeyCharacterToKeyCode('f')) {
var accuracy = 'incorrect';
}
/* Label unanswered trials */
} else {
var accuracy = 'unanswered';
}
data.accuracy = accuracy;
},
sample: {
type: 'without-replacement',
size: 4
}
};
feedback = {
type: 'html-keyboard-response',
stimulus: function () {
var last_trial_accuracy = jsPsych.data.getLastTrialData().values()[0].accuracy;
if (last_trial_accuracy == 'incorrect') {
return '<p style="color:red; font-face:bold;">X</p>';
} else if (last_trial_accuracy == 'unanswered') {
return '<p style="color:red; font-face:bold;">0</p>'
} else {
return ''
}
},
choices: jsPsych.NO_KEYS,
trial_duration: function () {
var last_trial_accuracy = jsPsych.data.getLastTrialData().values()[0].accuracy;
if (last_trial_accuracy == 'correct') {
return 0
} else {
return 800
}
}
};
var main_procedure = {
timeline: [fixation, primeWord, targetWord, feedback],
timeline_variables: [primeWord, targetWord]
};
timeline.push(main_procedure);
var debrief = {
type: 'html-keyboard-response',
choices: ['c'],
stimulus: function () {
var total_correct = jsPsych.data.get().filter({ accuracy: 'correct' }).count();
var total_incorrect = jsPsych.data.get().filter({ accuracy: 'incorrect' }).count();
var accuracy_rate = Math.round(total_correct / (total_correct + total_incorrect) * 100) + "%";
var total_unanswered = jsPsych.data.get().filter({ accuracy: 'unanswered' }).count();
var message = "<div style='font-size:20px;'><p>All done!</p>" +
"<p>Your accuracy rate was " + accuracy_rate + " (" + total_correct + " correct trials, " + total_incorrect +
" incorrect and " + total_unanswered + " unanswered).</p>" +
"<p>Press C to see the entire set of data generated by this experiment.</p></div>";
return message;
}
}
/* Add debrief to the timeline */
timeline.push(debrief);
/* Initialize experiment by incorporating the timeline and setting the data to be displayed at the end. */
jsPsych.init({
timeline: timeline,
on_finish: function () {
jsPsych.data.displayData();
},
default_iti: 250
});
</script>
</html>