Accessing global variables/ function as a timeline #718
-
Hello! I'm knew to JSPsych but pretty well versed in Python, R, and MatLab, so I'm a bit stumped as to why I'm having such a hard time adjusting to this whole timeline structure. I am creating a program that requires some initial calibration. It records how fast you can press a button in 5 seconds, and then takes the average. I use that average to create the choices for the following html-button-response objects. The choices are an array of choice objects, with the text for the options (ex. {choices: ["No presses for .50 cents", 23 presses for 20 euros"]}, etc.) My issues are the following:
I understand that JS code is loaded all at once but I can't seem to understand how to work with more dynamic parameters that need to be updated based on user performance. Once I solve this, I should be able to tackle most of my future problems. Below is my current code. Literally any help would be super appreciated. This package is so cool but I'm totally stumped!!! <!DOCTYPE html>
<html>
<head>
<title>Effort Discounting Draft</title>
<script src="jspsych-6.1.0/jspsych.js"></script>
<script src="jspsych-6.1.0/plugins/jspsych-instructions.js"></script>
<script src="jspsych-6.1.0/plugins/jspsych-html-keyboard-response.js"></script>
<script src="jspsych-6.1.0/plugins/jspsych-html-button-response.js"></script>
<script src="jspsych-6.1.0/jspsych-psychophysics-master/jspsych-psychophysics.js"></script>
<link href="jspsych-6.1.0/css/jspsych.css" rel="stylesheet"></link>
</head>
<body>
<div id="srt-score">
<span class="srt-top">Time: <progress max="100" value="0" id="srt-timer"></progress></span>
</div>
<div id="jspsych-target"></div>
</body>
<script>
/*************************************** JS Script **************************************/
/*GLOBAL VARIABLES*/
/* create the experiment timeline */
var inst_cal_timeline=[];
var effort_trials_timeline=[];
var full_timeline = [];
const cal_array=[]; //array with final number of clicks for the calibration phase
var cal_press=0;
/*this seems super helpful: https://www.jspsych.org/tutorials/rt-task/
var max_choice_time = 5000; /*assuming this is in milliseconds*/
var max_effort;
function set_max_effort(val){
max_effort=val;
}
function get_max_effort(){
return max_effort;
}
//for simplicity we'll start off with a low effort of .15
//and a reward level of .5 euros
//var choice_stim=[];
// function store_choice_stim(val){
// choice_stim=val;
// }
//
// function get_choice_stim(){
// return choice_stim;
// }
//default value
var choice_stim=[];
function make_stim(cal_effort){
console.log("thi sis what we are using for teh high effort: "+ cal_effort);
var high_effort_levels=[.60, .70, .80, .90, 1];
var high_reward_levels = [1, 2, 5, 10];
var high_reward_pairs= [];
//creating pairs
for (var i in high_effort_levels){
for (var j in high_reward_levels){
var pair=[high_effort_levels[i],high_reward_levels[j]];
high_reward_pairs.push(pair);
j++;
}
i++;
}
high_reward_pairs = jsPsych.randomization.shuffle(high_reward_pairs);
var direction = [];
for (let i=0; i<=high_reward_pairs.length; i++){
if (i<=Math.round(high_reward_pairs.length/2)){
direction.push("left");
}else{
direction.push("right");
}
}
direction = jsPsych.randomization.shuffle(direction);
///adding the random ddirection part to the array.
for (var i in high_reward_pairs){
high_reward_pairs[i].push(direction[i]);
i++;
}
for (var i = 0; i <high_reward_pairs.length; i++) {
console.log("In the max_stim function: " +cal_effort);
var high_effort_choice=Math.round(high_reward_pairs[i][0]* cal_effort);
var high_effort_text=high_effort_choice+" clicks for "+high_reward_pairs[i][1]+"\u20AC"
var low_effort_text="No effort for .50\u20AC";
if (high_reward_pairs[i][2]=="left"){
var butt_text=[low_effort_text, high_effort_text];
} else {
var butt_text=[high_effort_text, low_effort_text];
}
choice_stim.push({
choices: butt_text
});
}
console.log(choice_stim);
//store_choice_stim(choice_stim);
return choice_stim;
}
//ends function make stim
/*****************************************************************************/
/* EXPERIMENT SLIDES */
/*****************************************************************************/
/* instructions */
var inst = {
type: 'instructions',
pages: ['<p>This game requires you to make a series of decisions about rewards. '+
'You will need to choose between small rewards for no effort, or larger rewards'+
'for more effort.</p></p><p>Click <q>next</q> to continue.</p>',
'<p>The <q>effort</q> in this case will be a number of key presses within 20 seconds.'+
'<p>We are going to practice the task by going over the button pressing and then the choice portions of the task.</p>'+
'</p><p>Click <q>next</q> to continue.</p>'],
allow_keys: false,
show_clickable_nav: true
}
var inst_start_calibration = {
type: 'html-keyboard-response',
stimulus: '<p>In this portion, we want you to press the space bar as fast as you can for 20 seconds straight.</p>'+
'<p>There will be a timer to show you how much time you have left and a counter to show you'+
'how many times you have pressed the space bar.</p>'+
'</p><p>Get into position and <b>press the space bar</b> to start.</p>',
choices: [32],
response_ends_trial: true
}
/* STARTING CALIBRATION PHASE */
var cal_start= {
type: 'html-keyboard-response',
choices: [32], // space bar
on_load: function(){
cal_press=0;
},
stimulus:jsPsych.timelineVariable('inst_text'),
on_finish: function(data){
if(data.key_press == 32){
cal_press++;
data.press_num=cal_press;
trial_start_time = Date.now();
}
}
}
var cal_screen = {
type: 'html-keyboard-response',
choices:[32],
stimulus: function(){
return '<p>' + cal_press + ' presses</p>';
},
on_finish: function(data){
if(data.key_press == 32){
cal_press++;
data.press_num=cal_press;
}
},
is_html: true
}
var cal_loop = {
timeline: [cal_screen],
loop_function: function(){
if(Date.now() - trial_start_time < 5000){
return true;
} else {
return false;
}
}
}
var cal_fb_screen = {
type: 'html-button-response',
choices:["Next"],
stimulus: function(){
var final_press=jsPsych.data.get().last(1).values()[0].press_num;
console.log(final_press);
cal_array.push(final_press);
console.log(cal_array);
return ['<p>You pressed ' + final_press + ' in 20 seconds.</p>'+
'</p><p>Press <q>next</q> to continue.'];
},
}
var calibration_procedure = {
timeline: [cal_start, cal_loop, cal_fb_screen],
timeline_variables:[{inst_text: '<p>Start pressing!!</p>'},
{inst_text: '<p>Ok, try again. When you are ready... Start pressing!!</p>'},
{inst_text: '<p>Ok, try again. When you are ready... Start pressing!!</p>'},
{inst_text: '<p>Try one last time, and then we can practice the next portion.</p>'}],
randomize_order: false,
repetitions: 1 //I'm really repeating it 4 times using the different instruction texts
}
var inst_start_task = {
type: 'html-button-response',
stimulus: function(){
var sum = 0;
for (let i = 0; i < cal_array.length; i++){
sum += cal_array[i];
}
var avg_effort=Math.round(sum / cal_array.length);
jsPsych.data.addProperties({cal_effort: avg_effort});
// make_stim(avg_effort);
return '<p>Ok, the maximum number of presses you can make in 5 seconds is about ' + avg_effort+'.</p></p>'+
'<p>We will now start the real task. You will now make a series of choices about the rewards.</p>'+
'<p>Remember that sometimes you will have to do the effort and you will get a chance to win extra rewards,'+
'so please stay alert and answer honestly!</p>'+
'<p>Press <q>next</q> to start the real task.</p>';
},
choices:["Next"],
response_ends_trial: true
}
var var_pass_test = {
type: 'html-button-response',
stimulus:function(data){
var max_ef=jsPsych.data.get().last(1).values()[0].cal_effort;
var stim=make_stim(max_ef);
return '<p> Ok, so this is the stimuli: ' + stim +'</p>';
},
choices:["Next"],
response_ends_trial: true
}
/* create test trials */
// var choice_procedure={
// timeline:[
// {
// type: 'html-keyboard-response',
// stimulus: '<p style="font-size: 48px;">+</p>',
// choices: jsPsych.NO_KEYS,
// trial_duration: 2500
// },
//
// {
// type: 'html-button-response',
// stimulus:['<p>Which option do you prefer?</p>'],
// choices: timelineVariable("choices")
// }
//
// ],
//
// timeline_variables: choice_stim
// ]
// }
var fix = {
type: 'html-keyboard-response',
stimulus: '<p style="font-size: 48px;">+</p>',
choices: jsPsych.NO_KEYS,
trial_duration:function(){
return jsPsych.randomization.sampleWithoutReplacement([250, 500, 750, 1000, 1250, 1500, 1750, 2000], 1)[0];
}
}
var choice_pres= {
type: 'html-button-response',
stimulus:['<p>Which option do you prefer?</p>'],
timeline:function(){
console.log("Mom I made it!!");
var max_e=jsPsych.data.get().last(1).values()[0].cal_effort;
var stim=make_stim(max_e);
console.log(stim);
return stim;
}
//timeline:choice_stim;
}
///adding everything to timeline down here
full_timeline.push(inst);
full_timeline.push(inst_start_calibration);
full_timeline.push(calibration_procedure);
full_timeline.push(inst_start_task,var_pass_test);
full_timeline.push(fix, choice_pres);
jsPsych.init({
display_element: 'jspsych-target',
timeline: full_timeline,
/*on_finish: function(){ jsPsych.data.displayData('csv'); }*/
on_finish: function(){
jsPsych.data.get().localSave('csv','mydata.csv');
}
});
</script>
</html> |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 4 replies
-
Hi @plopezgamundi . I'm having a hard time figuring out where I need to focus my parsing of the code. Which part of the experiment shows the first sign of problems? I recorded a 30 minute video tutorial a few months ago that deals with more complex designs and dynamic parameters. It might help with some of the conceptual understanding. |
Beta Was this translation helpful? Give feedback.
-
I believe the problem here is that you're trying to dynamically generate trials by using a function as a nested timeline. I don't think that's possible. I'll defer to @jodeleeuw Also, minor bug, the stimulus parameter to html-button-response should be a string. |
Beta Was this translation helpful? Give feedback.
-
Thanks @Vijaygopal1234 for IDing the function as a timeline. That's a problem. You can't use a function as a timeline (yet). It looks like One way that you could keep most of the code in place but fix the var loop_index = 0;
var choice_pres= {
timeline: [{
type: 'html-button-response',
stimulus: ['<p>Which option do you prefer?</p>'],
choices: function(){ return stim[loop_index]; }
}],
loop_function: function(){
loop_index++;
return loop_index < stim.length; // repeats trial if true
}
} You'll need to define stim somewhere. A logical place would looks like the stimulus function of the var avg_effort=Math.round(sum / cal_array.length);
stim=make_stim(avg_effort); // no var keyword here so that variable has global scope. |
Beta Was this translation helpful? Give feedback.
Thanks @Vijaygopal1234 for IDing the function as a timeline. That's a problem. You can't use a function as a timeline (yet).
It looks like
make_stim()
is returning a whole set of choices objects. I think I understand what you are trying to do now.One way that you could keep most of the code in place but fix the
choice_pres
timeline is to useloop_function
You'll need to define stim somewhere.…