Computer Programming
For Psychologists

Image rating scales in jsPsych


Problem statement

In the upcoming monthts we will launch a series of online experiments in which participants are required to rate a series of images by responding to a set of Likert scales. We want the image to be displayed at the top with the rating scales underneath.

We picked the excellent jsPsych framework to help in our development. In jsPsych, you start by creating a timeline object to which you can add individual trials. These trials can be created using one of te many plugins that are available. 

In the documentation I found many plugins for displaying images, as well as a plugin to display a Likert scale. Unfortunately no plugin to combine an image with a Likert scale. Fortunately, I quickly discovered that the way jsPsych implements the Likert scale makes it almost trivial to combine this with an image.

 

A single trial

The code for creating a Likert scale looks as follows:

let rating_scale = ["Strongly agree", "Agree", "Neutral", "Disagree", "Strongly disagree"];

let image_rating = {
   'type': 'survey-likert',
   questions: [
      {prompt: "Generic statement", labels: rating_scale},
   ],
}

The questions property of the image_rating object is an array that contains information for each of the rating scales you want to present (here we only work with one rating scale). Specifically, each rating scale needs a prompt and a list of labels. It turns out that the text you provide for the prompt will be interpreted as HTML code. This means that if the HTML <img> tag is included, this will display the image referenced in that tag. This is shown in the code below, where I have also included a <br> tag to make the prompt for our question appear on a new line:

let rating_scale = ["Strongly agree", "Agree", "Neutral", "Disagree", "Strongly disagree"];
	
let image_rating = {
   'type': 'survey-likert',
   questions: [
      {prompt: "<img src='images/im_1.jpg'><br>Generic statement", labels: rating_scale},
   ],
}

 

Running multiple trials

From here it was straightfoward to go to multiple trials, each with its own image. You first create an array of images that you want to use. Then, you use a for-loop to iterate over this array and create a new image_rating object for each image.

To add the image name dynamically, I used a placeholder text in the prompt string which I then replace by the actual image name. 

There is one important thing I noticed at this point, and tthat is that the data that jsPsych records does not include the message displayed in the prompt. This means that if you use more than one image and if you start to randomize the order of your trials, you have no information about which stimulus was presented during a given trial. Again we were lucky because jsPsych allows you to easily add a data property to the image_rating object in which you can specify additional information that needs to be stored for each trial.

let images = ["images/im_1.jpg", "images/im_2.jpg"];
	
let rating_scale = ["Strongly agree", "Agree", "Neutral", "Disagree", "Strongly disagree"];
	
for (i = 0; i < images.length; ++i){
   let current_image = images[i];
   let image_rating = {
      'type': 'survey-likert',
      questions: [
         {prompt: "<img src='%img%'><br>Generic statement".replace('%img%', current_image), labels: rating_scale},
      ],
      data: {
         stimulus: current_image
      },
   }
   timeline.push(image_rating);
}

 

Preloading images

When your experiment contains a lot of images, it is good practice to preload these images. Preloading simply means that all the images are downloaded to the clients device before the experiment is started. This solves the problem where you might see an image being loaded during a trial (which could happen because of a slower internet connection).

jsPsych automatically preloads images and other media that are part of the standard plugins. Since here we adjusted the prompt of a Likert scale to include an image, jsPsych will not detect this image and we have to do the preloading manually. Once again, this is very easy to accomplish. All we need to do is to pass our images array to the preload_images parameter in the jsPsych initialization function and we are good to go:

let images = ["images/im_1.jpg", "images/im_2.jpg"];

jsPsych.init({
   timeline: timeline,
   preload_images: images
})