adaptive trial_duration, conditional trials and randomisation of timelines

413 views
Skip to first unread message

MagdaLena Matyjek

unread,
Jul 5, 2018, 11:35:34 AM7/5/18
to jsPsych
Hey! 
I really hope you can help me. I spent SO much time reading all the sources, but my problems seem to be complicated and connected to each other (that's why I put a few questions in one thread). I'll appreciate help a lot!

In my experiment in each trial participants see first a CUE, then a FIXATION CROSS, then a TARGET, then a fixation cross again (called PREFEEDBACK) and finally FEEDBACK (and then ITI).  All of these are either image-keyboard-response or html-keyboard-response. I have 3 problems with that:

1) The participants have to press spacebar while the TARGET is on the screen. The target is presented for an adaptive amount of time, meaning that it starts with a certain value and if a participant manages to press space while it's on the screen, their response was correct. If they pressed after it disappeared, the response was incorrect. After each 5 repetitions of the timeline the procedure checks whether most of the responses were correct, and then if yes, then the display time of the target decreases (to make it harder); if no - the display time increases (to make it easier). The timeline should be repeated 40 times.

How do I implement this adaptive display time? How can store and access the last 5 responses to this trial only (in a more complicated timeline) and adjust trial_duration to this? I have tried with jsPsych.data.get().last(1).rt, but  it doesn't seem to do what I need.


2) The FEEDBACK depends on whether the last response to TARGET was correct. If so, I want to use image-keyboard-response with picture A and if the response was not correct - a image-keyboard-response with picture B.

How to retrieve the "correct" information and display a trail depending on this value? I read "Conditional timelines" on the website, but again - I can't get it work. I guess if I could pass the "correct" variable from the target between subsequent trials it would solve the problem.


3) The experiment has 3 conditions. I failed miserably to use timelineVariables, so decided to prepare one timeline for each condition and somehow randomise the order of their display. 

How can I randomise timelines?

------------------------------------------------------------------------------
This is my code in a simplified version of the timeline (not addressing the 3 problems):


    var timeline = [];
    
    var procedure_M = {
        timeline: [
         /* CUE */
            {
                type: "image-keyboard-response",
                stimulus: "img/M.png",
                choices: jsPsych.NO_KEYS,
                trial_duration: 1000,
                data: {cond: 'M', test_part: 'cue'}
            },

        /* FIX */
            {
                type: 'html-keyboard-response',
                stimulus: '<div style="font-size:60px;">+</div>',
                choices: jsPsych.NO_KEYS,
                trial_duration: 500,
                data: {test_part: 'fixation'}
            },

        /* TARGET */
            {
                type: "image-keyboard-response",
                /*stimulus: jsPsych.timelineVariable('stimulusTest'),*/
                stimulus: "img/blue.png",
                choices: ['spacebar'],
                trial_duration: target_time,
                data: {test_part: 'target'},
            },

        /* PREFEEDBACK */
            {   
                type: 'html-keyboard-response',
                stimulus: '<div style="font-size:60px;">+</div>',
                choices: jsPsych.NO_KEYS,
                trial_duration: function(){
                  return jsPsych.randomization.sampleWithoutReplacement([250, 500, 750, 1000, 1250, 1500, 1750, 2000], 1)[0];
                },
                data: {test_part: 'prefeedback', cond: 'M'}
            },

        /* FEEDBACK */
            {
                type: "image-keyboard-response",
                stimulus: "img/Mpos.png",
                choices: jsPsych.NO_KEYS,
                trial_duration: 1000,
                data: {test_part: 'feedback', cond: 'M', val: 'pos'}
            },

        /* ITI */
            {
                type: 'html-keyboard-response',
                stimulus: '<div style="font-size:0px;">+</div>',
                choices: jsPsych.NO_KEYS,
                trial_duration: 500,
                data: {test_part: 'iti'}
            }
        ],
        repetitions: 3
    }

    timeline.push(procedure_M);

    /* start the experiment */
    jsPsych.init({
      timeline: timeline,
      show_progress_bar: true,
      on_finish: function() {
        jsPsych.data.displayData();
      }
    });


------------------------------------------------------------------------------
I used jspych before (version 5.0.3), but the update is terribly unintuitive for me and I'm afraid I can't manage without help.

Thank you in advance!

Josh de Leeuw

unread,
Jul 5, 2018, 12:01:24 PM7/5/18
to MagdaLena Matyjek, jsPsych
How do I implement this adaptive display time? How can store and access the last 5 responses to this trial only (in a more complicated timeline) and adjust trial_duration to this? I have tried with jsPsych.data.get().last(1).rt, but  it doesn't seem to do what I need. 

To access the last 5 responses to that trial only, you can filter on the test_part data property that you added to the trials. For example:

jsPsych.data.get().filter({test_part: 'target'}).last(5).select('rt').values;

This will return an array with the five most recent rt values. There are also some functions besides values that you can call (https://www.jspsych.org/core_library/jspsych-data/#datacolumn).

To change the display time based on this result, you'll want to make the trial_duration a function. For example:

var trial = {
  trial_duration: function(){
    var mean_rt = jsPsych.data.get().filter({test_part: 'target'}).last(5).select('rt').mean();
    if(mean_rt < 1000) { return 2000; } else { return 3000; }
  }
}

Note that you can reference variables that are defined outside the trial inside these functions. This can provide some easy continuity between trials.

var last_trial_duration = 1000;
var trial = {
  trial_duration: function(){
    var mean_rt = jsPsych.data.get().filter({test_part: 'target'}).last(5).select('rt').mean();
    if(mean_rt < 1000) { 
      last_trial_duration = last_trial_duration / 2;
    } else {
      last_trial_duration = last_trial_duration * 2;
    }
    return last_trial_duration;
  }
}

How to retrieve the "correct" information and display a trail depending on this value? I read "Conditional timelines" on the website, but again - I can't get it work. I guess if I could pass the "correct" variable from the target between subsequent trials it would solve the problem.  

If the only thing you want to change is the picture, then you can make the stimulus parameter of the trial a function, like in the above example.

stimulus: function(){
  var correct = jsPsych.data.get().filter({test_part: 'target'}).last(1).select('correct').values[0];
  if(correct) {
    return 'image_correct.png';
  } else {
    return 'image_incorrect.png';
  }
}

How can I randomise timelines?  

Just change the order in which you add them to the main timeline. For example:

var all_three_timelines = [timeline_a, timeline_b, timeline_c];
var random_order = jsPsych.randomization.shuffle(all_three_timelines);
timeline = timeline.concat(random_order);

 

--
You received this message because you are subscribed to the Google Groups "jsPsych" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jspsych+u...@googlegroups.com.
To post to this group, send email to jsp...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jspsych/c6f52ff5-ef3f-44bb-a763-c81d75359bac%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

MagdaLena Matyjek

unread,
Jul 10, 2018, 2:48:22 PM7/10/18
to jsPsych
Hey Josh!

Thank you so much, this is great! I managed to overcome most of my problems now, but I was hoping you could help me with another thing.
I have to make sure my participants don't press the space all the time (especially before the target) to be faster, so I want to display a message "Wait for the dot!" when they press it before the target appear.
I tried to do it by manipulating the duration time - 800ms when they indeed press the key, or 0ms when they behave.
However, this doesn't seem to work. I guess because of the refresh rate sometimes when it's supposed to be 0, you can see one frame anyway.
How do I do it better?
I read conditional timelines, which would be perfect, but I want conditional trials, not timelines.

        /* FIX */
            {
                type: 'html-keyboard-response',
                stimulus: '<div style="font-size:60px;">+</div>',
                choices: ['spacebar'],
                trial_duration: 500,
                data: {test_part: 'fixation', correct_response: 'space'},
                on_finish: function(data){
                    data.correct = data.key_press == jsPsych.pluginAPI.convertKeyCharacterToKeyCode(data.correct_response);
                },
            },

        /* FIX - early press */
            {
                type: 'html-keyboard-response',
                stimulus: '<p> Wait for the dot to appear on the screen! </p>',
                choices: jsPsych.NO_KEYS,
                data: {test_part: 'fixation_error', correct_response: 'space'},
                /*trial_duration: 800,*/
                trial_duration: function(data) {
                    var correct = jsPsych.data.get().filter({test_part: 'fixation'}).last(1).select('correct').values[0];
                    var duration = 0;
                      if(correct) {
                        duration = 800;
                      } else {
                        duration = 0;
                      }
                      return duration;
                },
            },

Thank you in advance!
Lena

Josh de Leeuw

unread,
Jul 10, 2018, 2:51:05 PM7/10/18
to MagdaLena Matyjek, jsPsych
You could just have a single trial in the conditional timeline. Timelines can any size, so it's completely within the intended scope of conditional timelines to have a single trial.

--
You received this message because you are subscribed to the Google Groups "jsPsych" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jspsych+u...@googlegroups.com.
To post to this group, send email to jsp...@googlegroups.com.

Lena Matyjek

unread,
Jul 10, 2018, 2:57:54 PM7/10/18
to Josh de Leeuw, jsPsych

Does it mean I have to build one timeline for CUE, one for FIX, one for FIX – early press and one for TARGET-PRE-FEEDBACK-FEEDBACK-ITI, and then put them together to repeat multiple times?...

Josh de Leeuw

unread,
Jul 10, 2018, 3:04:53 PM7/10/18
to Lena Matyjek, jsPsych
No, you can mix timelines and trials together so you can have a main timeline that contains the trials and the conditional timeline, e.g.:

var timeline = [cue, fix, conditional_fix_timeline, ... ] 

Josh de Leeuw

unread,
Jul 11, 2018, 11:43:27 PM7/11/18
to Lena Matyjek, jsPsych
I think this line is causing the problem:

 timeline_exp = timeline.concat(timeline_exp); 

The concat function is adding the timeline_exp to the main timeline, but you do that (correctly) on the next line with .push(). Try removing the concat line, which I don't think you need if I'm understanding the structure correctly. 

On Tue, Jul 10, 2018 at 3:21 PM Lena Matyjek <lena.m...@gmail.com> wrote:

I tried that, but I’m having problems with calling the timelines. I define an empty one in the beginning, push instructions there, then create a trial structure as one timeline and push it on this general timeline. Simply nothing happens. What am I doing wrong?  

 

 

 

/* create timeline */

    var timeline = [];

 

    /* define welcome message trial */

    var focus = {

      type: "html-keyboard-response",

      stimulus: "The experiment is about to begin. Focus and when you are ready, press any key to begin."

    };

    timeline.push(focus);

 

    /* define instructions trial */

    var instructions = {

    };

    timeline.push(instructions);

 

 

    /* stimuli */

    var target_time = 300;

    var trial_no = 0;

    var tochange = 0;

    var trials_per_block = 5;

 

         /* CUE */

            var CUE_M = {

            }

 

        /* FIX */

            var FIX = {

            }

 

        /* FIX - early press

            {

            },

 

        /* TARGET */

            var TARGET = {

            }

 

        /* PREFEEDBACK */

            var PREFEEDBACK = {  

            }

 

        /* FEEDBACK */

            var FEEDBACK = {

            }

 

        /* ITI */

            var ITI = {

            }

 

           var timeline_exp: [CUE_M, FIX, TARGET, PREFEEDBACK, FEEDBACK, ITI];

           timeline_exp = timeline.concat(timeline_exp);

 

           timeline.push(timeline_exp);

 

 

    /* start the experiment */

 

 

    jsPsych.init({

      timeline: timeline,

      on_finish: function() {

        jsPsych.data.displayData();

      }

    });

lena.m...@gmail.com

unread,
Jul 17, 2018, 7:48:13 AM7/17/18
to jsPsych
On Thursday, 5 July 2018 17:35:34 UTC+2, MagdaLena Matyjek wrote:
All working, thank you so much!
Reply all
Reply to author
Forward
0 new messages