Computer Programming
For Psychologists

Updating the video-slider-response to track the history of responses


I have recently gotten into the habit of visiting the jsPsych discussion section on Github to see if I can help out with a few questions here and there. One recent post that caught my attention asked if it was possible to include a history of responses for the video-slider-response plugin. By default the plugin only records the latest position of the slider as data. However, the slider component in PsychoPy does include does option, and I can imagine that there would definitely be a few use cases where having this option would be a good thing to have.

I therefore thought it would be interesting to see how easy it would be to update the plugin so that it includes this functionality. In the end, I only had to make three changes to the code in order for it to work, and I will discuss them one by one below. The version of jsPsych that I am using here is 7.3.1

Adding a property to configure the behavior

Because most users probably do not need to record the history of responses, I first of all wanted to make it so that recording the history could be enabled by setting a property. So by default it would have the original behavior of only recording the last position of the slider. By adding a property named track_slider_history and setting its value to true, the stored data will record all position that have been set. 

To make this change, I added the property to the info structure that is provided in the of the plugin-video-slider-response.js file. For this I used the following code and added it after the definition of the response_allowed_by_playing property:

/** If true, then all changes to the slider position will be recorded in an array. */
track_slider_history: {
    type: jspsych.ParameterType.BOOL,
    pretty_name: "Record the history of slider values",
    default: false
}

What we basically do is define a dictionary with the name of the property that we want to add. In this dictionary we specify the type of this property, a nicely formatted string that describes what this property does, and a default value of false (because by default we only want to have the final response).

Create a callback to trigger storing the current response

Once we have the property defined, the next step is to use this property in the trial() function of the VideoSliderResponsePlugin class. The trial() function is responsible for initializing and displaying the stimulus, and defining all its behaviors. Part of this initialization procedure is setting up how the data output will look. By default, this looks as follows:

// store response
var response = {
    rt: null,
    response: null,
};

Because the data will look different if tracking history is enabled, I added the following code after the definition of this variable:

if (trial.track_slider_history){
	response = {
		rt: [],
		response: []
	};

	const record_slider_position = () => {
		response.rt.push(Math.round(performance.now() - startTime));
		response.response.push(display_element.querySelector("#jspsych-video-slider-response-response").valueAsNumber);
	};

	document.querySelector("#jspsych-video-slider-response-response")
		.addEventListener("change", record_slider_position);
}

There are three parts to this code, and they will be executed when the track_slider_history is enabled. First, the response variable is updated and now uses an array for the reaction time and responses.

We then define a function record_slider_position, which adds a new reaction time, as well as the current position of the slider to the response arrays. 

Finally, we select the slider input and assign the function that we just created as a callback for the change event. The effect of this is that every time the slider value changes, it will execute our record_slider_position function and push new values to the arrays.

Disable default behavior

The default behavior of the plugin is to record the reaction time and the response when the next button is clicked. This is done by assigning an event listener for the "click" event jspsych-video-slider-response-next component. When we are recording the history of responses however, we do not want the default behavior to overwrite the arrays that we are using. Therefore, I added a check in this part of the code for the value of the trial.track_slider_history variable, and only allow the default behavior when it is not set to true:

display_element
  .querySelector("#jspsych-video-slider-response-next")
  .addEventListener("click", () => {
  // measure response time but only when history is not tracked
  if (!trial.track_slider_history){
	var endTime = performance.now();
	response.rt = Math.round(endTime - startTime);
	response.response = display_element.querySelector("#jspsych-video-slider-response-response").valueAsNumber;
  }
  if (trial.response_ends_trial) {
	  end_trial();
  }
  else {
	  display_element.querySelector("#jspsych-video-slider-response-next").disabled = true;
  }
});

In action

The following code uses the plugin with the recording of the history enabled, and shows the data when the trial is finished.

const video_trial = {
	type: jsPsychVideoSliderResponse,
	stimulus: ['video.webm'],
	track_slider_history : true,
	on_finish: function(data) {
		console.log(data);
	}
}

Conclusion

As you can see, the jsPsych plugin system offers a very flexible way to adjust the behavior of existing plugins. With just a few chances we have extended the functionality of the video-slider-response so that we can choose between either collecting the final position at the end of the trial, or a history of all responses since the start of the video.

The Github discussion thread can be found here: https://github.com/jspsych/jsPsych/discussions/2939