Skip to content

Dynamic parameters

Most trial parameters can also be specified as functions. In a typical declaration of a jsPsych trial, parameters are known at the start of the experiment. This makes it impossible to alter the content of the trial based on the outcome of previous trials. However, when functions are used as the parameter value, the function is evaluated right before the trial starts, and the return value of the function is used as the parameter value for that trial. This enables dynamic updating of the parameter based on data that a participant has generated or any other information that you do not know in advance.

Examples

Providing Feedback

Here is a sketch of how this functionality could be used to display feedback to a participant in the Flanker Task.

var timeline = [];

var trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: '<<<<<',
  choices: ['f','j'],
  data: {
    stimulus_type: 'congruent',
    target_direction: 'left'
  },
  on_finish: function(data){
    // Score the keyboard response as correct or incorrect.
    if(jsPsych.pluginAPI.compareKeys(data.response, "f")){
      data.correct = true;
    } else {
      data.correct = false; 
    }
  }
}

var feedback = {
  type: jsPsychHtmlKeyboardResponse,
  trial_duration: 1000,
  stimulus: function(){
    // The feedback stimulus is a dynamic parameter because we can't know in advance whether
    // the stimulus should be 'correct' or 'incorrect'.
    // Instead, this function will check the accuracy of the last response and use that information to set
    // the stimulus value on each trial.
    var last_trial_correct = jsPsych.data.get().last(1).values()[0].correct;
    if(last_trial_correct){
      return "<p>Correct!</p>"; // the parameter value has to be returned from the function
    } else {
      return "<p>Wrong.</p>"; // the parameter value has to be returned from the function
    }
  }
}

timeline.push(trial, feedback);

Note

When scoring a participant's response, the jsPsych.pluginAPI.compareKeys function is only needed to compare keyboard responses. For other kinds of response types, such as button presses, you can compare the participant's response and correct response values directly, e.g.

if (data.response == 0)){
  data.correct = true;
} else {
  data.correct = false; 
}
Or:
data.correct = data.response === data.correct_response;

Randomizing a parameter value

Here's an example of using a dynamic parameter to randomize the inter-trial interval (ITI) duration. This time, the dynamic parameter is created using a named function instead of an anonymous function.

var random_duration = function() {
    var rand_dur = jsPsych.randomization.sampleWithoutReplacement([500,600,700,800],1)[0];
    return rand_dur;
}

var trial = {
    type: jsPsychHtmlKeyboardResponse
    stimulus: '+',
    post_trial_gap: random_duration  // if you use a named function for a dynamic parameter, then just use the function name (without parentheses after it)
}

Storing changing variables in the data

The trial's data parameter can be also function, which is useful for when you want to save information to the data that can change during the experiment. For example, if you have a global variable called current_difficulty that tracks the difficulty level in an adaptive task, you can save the current value of this variable to the trial data like this:

var current_difficulty; // value changes during the experiment

var trial = {
  type: jsPsychSurveyText,
  questions: [{prompt: "Please enter your response."}]
  data: function() { 
    return {difficulty: current_difficulty}; 
  }
}

It's also possible to use a function for any of the individual properties in the trial's data object, for instance if you want to combine static and dynamic information in the data:

var trial = {
  type: jsPsychSurveyText,
  questions: [{prompt: "Please enter your response."}]
  data: {
    difficulty: function() { 
      return current_difficulty; // the difficulty value changes during the experiment
    },
    task_part: 'recall', // this part of the data is always the same
    block_number: 1
  }
}

Nested Parameters

Dyanmic parameters work the same way with nested parameters, which are parameters that contain one or more sets of other parameters. For instance, many survey-* plugins have a questions parameter that is a nested parameter: it is an array that contains the parameters for one or more questions on the page. To make the questions parameter dynamic, you can use a function that returns the array with all of the parameters for each question:

var subject_id; // value is set during the experiment

var trial = {
  type: jsPsychSurveyText,
  questions: function(){
    var questions_array = [ 
        {prompt: "Hi "+subject_id+"! What's your favorite city?", required: true, name: 'fav_city'},
        {prompt: "What is your favorite fruit?", required: true, name: 'fav_fruit'},
    ];
    return questions_array;
  }
}

You can also use a function for any of the individual parameters inside of a nested parameter.

var trial = {
  type: jsPsychSurveyText,
  questions: [
    { 
      prompt: function() {  
        // this question prompt is dynamic - the text that is shown 
        // will change based on the participant's earlier response
        var favorite_city = jsPsych.data.getLastTrialData().values()[0].response.fav_city;
        var text = "Earlier you said your favorite city is "+favorite_city+". What do you like most about "+favorite_city+"?"
        return text;
      }, 
      required: true,
      rows: 40,
      columns: 10
    },
    { prompt: "What is your favorite fruit?", required: true, name: 'fav_fruit' }
  ]
}

When dynamic parameters can't be used

Note that if the plugin expects the value of a given parameter to be a function, then this function will not be evaluated at the start of the trial. This is because some plugins allow the researcher to specify functions that should be called at some point during the trial. Some examples of this include the stimulus parameter in the canvas-* plugins, the mistake_fn parameter in the cloze plugin, and the stim_function parameter in the reconstruction plugin. If you want to check whether this is the case for a particular plugin and parameter, then the parameter's type in the plugin.info section of the plugin file. If the parameter type is ParameterType.FUNCTION, then this parameter must be a function and it will not be executed before the trial starts.

Even though function evaluation doesn't work the same way with these parameters, the fact that the parameters are functions means that you can get the same dynamic functionality. These functions are typically evaluated at some point during the trial, so you still get updates to values within the function during the trial.