Adding New Events in the Middle of a jsPsych Timeline

111 Views Asked by At

Goal

This simple demonstration should generate a task using jsPsych that ideally displays two or three screens, depending upon the choices made by the participant. The first event, ChoiceTrial, presents individuals with three buttons, labelled 'A', 'B', and 'C'. If A is pressed, a concluding message, Conclusion, ending the experiment is displayed. If any button but A is pressed, an interim message, Message, indicating A was not pressed should be displayed, followed by the concluding message.

Issue

While the on_finish logic of the ChoiceTrial event appears to make sence, because the timeline is defined and ran before any user input is collected, I cannot add the Message event contingent upon participant button selections.

Question

How could I dynamically add new events, based upon participant responses, in the middle of a jsPsych Timeline?

Solutions Tried

I initially programmed the task to simply as a timeline.push conditional upon 'A' not being selected. This of course did not work. I then tried coding a function which would insert a few event into the timeline by finding the current event index and splicing in a new event, and then callign this new function on_finish. This also did not work:

// Ideally a function which should modify the timeline and insert a new trial 
function insertTrialAfterCurrent(newTrial) {
    const currentTrialIndex = jsPsych.getProgress().current_trial_global;
    timeline.splice(currentTrialIndex + 1, 0, newTrial);
}

Lastly, I've tried switching to creating a new timeline, pushing to that new timeline, and then using the jsPsych.addNodeToEndOfTimeline function to add to the main timeline, as suggested in this forum post, but again no dice.

Example Code

I believe this example should be reproducible and demonstrate my issue. Thanks for your time and effort!

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <!-- Experiment Title -->
  <title>Experiment</title>

  <!-- Loading in libraries  -->
  <script src="https://unpkg.com/[email protected]"></script>
  <script src="https://unpkg.com/@jspsych/[email protected]"></script>
  <script src="https://unpkg.com/@jspsych/[email protected]"></script>
  <link href="https://unpkg.com/[email protected]/css/jspsych.css" rel="stylesheet" type="text/css" />   
</head>

<!-- Determing general page style parameters -->
<body style="background-color: rgb(200, 200, 200);">
<script>
    
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |
// | - - - - - - - - EXPERIMENT VARIABLES - - - - - - -  |
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |   

// Initiating jsPsych
var jsPsych = initJsPsych({
  timeline: timeline,
});
    
// Creating an array of three choices, which we'll randomize the order of
var choice = jsPsych.randomization.sampleWithoutReplacement(['A', 'B', 'C'], 3);  

// | - - - - - - - - - - - - - - - - - - - - - - - - - - |
// | - - - - - - - - EXPERIMENT FUNCTIONS  - - - - - - - |
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |  

// Creating an empty array which will act as our timeline
var timeline = [];

// Creating some message that will display if any button except A is pressed
var Message = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'Congrats, It worked! Press Space to Continue',
  choices: [' '],
};

// Creating a concluding message to indicate that the experiment has concluded
var Conclusion = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'The experiment Ended! Press Space to Continue',
  choices: [' '],
};

// Presenting three choices to choose from
var ChoiceTrial = {
  type: jsPsychHtmlButtonResponse,
  stimulus: 'Choose A to end, or B or C to see a message',
  choices: choice,
  on_finish: function (data) {
    var new_timeline = {
        timeline: [Message]
    }
    jsPsych.addNodeToEndOfTimeline(new_timeline)
    }
};

// | - - - - - - - - - - - - - - - - - - - - - - - - - - |
// | - - - - - - - - EXPERIMENT TIMELINE - - - - - - - - |
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |    

  // Adding the choice trial to the timeline
  timeline.push(ChoiceTrial);

  // Adding the conclusion to the timeline
  var new_timeline = {
    timeline: [Conclusion]
  }
  jsPsych.addNodeToEndOfTimeline(new_timeline)

// | - - - - - - - - - - - - - - - - - - - - - - - - - - |
// | - - - - - - - - RUNNING EXPERIMENT  - - - - - - - - |
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |   

    // Executing the timeline
    jsPsych.run(timeline);

</script>
</body>
</html>
1

There are 1 best solutions below

0
William Mitchell On BEST ANSWER

Josh de Leeuw directed me to a solution for this specific problem. The missing piece was conditional functions, which allow me to add events that either are included or skipped depending upon responses to questions that precede the conditional one on the timeline. See the solution I used below:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <!-- Experiment Title -->
  <title>Experiment</title>

  <!-- Loading in libraries  -->
  <script src="https://unpkg.com/[email protected]"></script>
  <script src="https://unpkg.com/@jspsych/[email protected]"></script>
  <script src="https://unpkg.com/@jspsych/[email protected]"></script>
  <link href="https://unpkg.com/[email protected]/css/jspsych.css" rel="stylesheet" type="text/css" />   
</head>

<!-- Determing general page style parameters -->
<body style="background-color: rgb(200, 200, 200);">
<script>
    
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |
// | - - - - - - - - EXPERIMENT VARIABLES - - - - - - -  |
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |   

// Initiating jsPsych
var jsPsych = initJsPsych({
  timeline: timeline,
});
    
// Creating an array of three choices, which we'll randomize the order of
var choice = jsPsych.randomization.sampleWithoutReplacement(['A', 'B', 'C'], 3);  
var jsPsych = initJsPsych();


// | - - - - - - - - - - - - - - - - - - - - - - - - - - |
// | - - - - - - - - EXPERIMENT FUNCTIONS  - - - - - - - |
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |  

// Creating an empty array which will act as our timeline
var timeline = [];

// Creating some message that will display if any button except A is pressed
var Message = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'Congrats, It worked! Press Space to Continue',
  choices: [' '],
};

// Creating a concluding message to indicate that the experiment has concluded
var Conclusion = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'The experiment Ended! Press Space to Continue',
  choices: [' '],
};

// Presenting three choices to choose from
var ChoiceTrial = {
  type: jsPsychHtmlButtonResponse,
  stimulus: 'Choose A to end, or B or C to see a message',
  choices: choice,
};

// Incoporating a conditional function
var if_node = {
  timeline: [Message],
  conditional_function: function(){
      // If the last value recorded within our data matches the index of choice A ...
      if (jsPsych.data.get().last(1).values()[0].response ==  choice.indexOf('A')) {
          // ... do not run this node within the timeline
          return false;
      } 
      // Otherwise ...
      else {
          // ... do run this node in the timeline
          return true;
      }
  }
};

// | - - - - - - - - - - - - - - - - - - - - - - - - - - |
// | - - - - - - - - RUNNING EXPERIMENT  - - - - - - - - |
// | - - - - - - - - - - - - - - - - - - - - - - - - - - |   

    // Executing the timeline
    jsPsych.run([ChoiceTrial, if_node, Conclusion]);

</script>
</body>
</html>