Last week, we learned how to build simple web pages using R Markdown. Over the next few weeks, we will learn to build more complex web pages using a combination of HTML and JavaScript. In particular, we will learn to use a JavaScript library called JSPsych. (Note: A library is a little like an R package – a set of functions for doing useful tasks in a programming language). JSPsych is created by Josh de Leeuw, and this tutorial is based on his introduction.

1 Set up your lab workspace

Create a new directory for this week’s lab, and then create a subdirectory called SimpleExpt. Download the lab zip file from Learn. Place it in the SimpleExpt directory and unzip it.

What does the zip file contain?

  • JSpsych folder (this is the JSPsych library), which has a few components (look in the folder)
  • The folder css contains jspsych.css; a css file contains layout instructions for a webpage (e.g., how large should the default text be; how should the different elements be arranged).
  • The file jspsych.js is a JavaScript file (that’s what .js stands for). It contains a set of functions for accessing all the other aspects of the JSPsych package.
  • The folder plugins contains a large number of .js files. Each file (called a plugin here) contains JavaScript code, and that code provides a template for specifying the appearance of a webpage and how the user can respond to that webpage (e.g., should the website display an image? should the user press a button in response to that image?)
  • The folder tests&examples contains web pages that exemplify how JSPsych can be used. Try opening the file demo-simple-rt-task.html in your web browser.

In this lab, you will go through some instructions for how to create that website. In the next lab, we will significantly modify the code, in order to turn that website into a game for a tablet computer.

2 Create your first webpage

At its simplest, a web page is simple an HTML document. HTML stands for hyper-text mark-up language; so, a web page is a mark up document (like R Markdown!), but one that also has hyper-links embedded in the text.

A neat fact about R Studio is that it can be used to edit both HTML documents and JavaScript documents. So, we can continue to use R Studio for this entire project.

Create an HTML file in R Studio. Go to File > New > R HTML. Save the file (File > Save) in the lab directory (call it, e.g., Simple_Expt). You’ll note that this isn’t really an HTML file, but an RHTML file. An RHTML file is an R file that can be used to generate an HTML document; you’ll learn more about this in a moment.

Basic R HTML file

Basic R HTML file

Let’s run through the components of the web page.

2.1  Tags.

Information in the web page is stored between tags. Tags are indicated by angled brackets, like <title>, and information within the tag will be processed accordingly (this is similar to R Markdown, where we could indicate that text would be in boldface by putting within ** tags). For example, if you look at the top of the document, you will see that there is an <html> tag; this indicates that all the subsequent information in the document will be HTML until the tag closes. Closing tags include a forward slash, like `

.

Another example is the title tag <title> </title>. This defines the name of the webpage (the name you see above your web browser’s menu bar). Try editing the title, and then let’s build the webpage. Press knit; this causes an HTML file to be created and saved in your folder. Then, from the RStudio preview, press the little arrow button on the menu bar, to send the HTML file into your web browser. You should be able to see your title

Basic R HTML file

Basic R HTML file

2.2 Body

The information between the <body> </body> tags is displayed in the web browser. In this file, that information is of a few different types.

  • The <p> tags define a paragraph of text.
  • The <b> tags make the text boldface.
  • The <rcode> tags allow you to insert R code into your website. We won’t be using that for now, however.

3 Create a simple experiment

This tutorial is drawn from Josh de Leeuw’s JSPsych documentation. You will be creating a simple web experiment in which your participants see a line of text, and press a button to make it disappear.

Delete all the material within the `

tags, so your HTML file looks like this: Basic R HTML file

3.1  Import some important JavaScript libraries

Look at your HTML file. Do you see the <head> tags? The area between these tags is similar to the Header on an RMarkdown document, and it is in this area where we can import our different libraries.

We need to import two different JavaScript libraries.

  • JSPsych – which will be used for programming the experiment
  • JQuery – which is a dependency for JSPsych, i.e., JSPsych uses functions from JQuery to help run experiments

And we also need to import the JSPsych .CSS file (remember CSS from earlier?)

We can import these two libraries and the CSS using the following HTML commands:

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script src="jspsych-5.0.3/jspsych.js"></script>
  <link href="jspsych-5.0.3/css/jspsych.css" rel="stylesheet" type="text/css"></link>

The first line imports jquery from a remote website run by Google. The second line imports JSPsych from the folder on your computer (you need to ensure that your HTML file, and the jspsych-5.0.3 folder are in the same directory). The third line imports the CSS file (CSS files aren’t strictly speaking “scripts”, so they aren’t loaded using the <script> command).

Knit your webpage. It should compile, but you should not see any text (because the body is blank).

3.2 Use a JSPsych plugin to print a line of text

Remember plugins from before? Now, we will use a plugin to print some text on your webpage.

3.2.0.1  Import text plugin

First, you need to import the text plugin (you’ll notice that there’s a lot of importing when using JSPsych!). Take a look at this code below, and try to explain to yourself what it does.

<script src="jspsych-5.0.3/plugins/jspsych-text.js"></script>

This file also needs to go in the Head of your document, along with the calls to the other .js files.

3.2.0.2 Edit the body of your webpage.

Remember the body tags? We are going to add some code, which the website will display. Importantly, that code will be JavaScript code, rather than HTML code. So, we add some script tags that go below the body tags. The code in the <script> tags will generate data that will be placed in the <body> tags, and displayed to participants. Your HTML should now look like this:

<body>
</body>
<script>


</script>

3.2.0.3 Call your JSPsych text plugin

Add the following JavaScript within the <script> tags. This code 1. Defines (i.e., creates) a variable called hello_trial (that’s the bit that says var hello_trial) 2. Assigns some information to that variable (the = {...} part). In particular, it assigns two key:Value pairs + A Key called type which has the value text + A Key called text which has the value “Hellow World!”

The type key defines this as an object created by the JSPsych Text plugin, which you imported before. The Text key defines what text should be displayed.

Note These Key: Value pairs are surrounded by curly braces {}; the braces bind these values together into a single object.

    var hello_trial = {
        type: 'text',
        text: 'Hello world!'
    }

3.2.1 Tell JSPsych to display your text plugin

The previous section created a variable that contains information about the text plugin. Now, we have to tell the website to show that information to the user.

The code below will do this. It calls a function called jsPsych.init().

  • This function takes an object as its argument, which contains some key:value pairs.
  • The most important key is timeline.
  • Timeline indicates the order in which your JSPsych plugins will be shown on the webpage (at the moment, you only have one plugin, but this will be critical later.
  • The value of the key timeline is an array.
  • An array is a collection of variables (like the c() command in R). In this case, the variables are collected together using square braces [].
  • Below, you’ll see that the array contains a single value, hello_trial

This code should be put below var hello_trial

    jsPsych.init({
        timeline: [ hello_trial ]
    })

Note jsPsych.init() is one of the JSPsych functions that was imported when you called <script src="jspsych-5.0.3/jspsych.js"></script>

3.2.1.1 Knit your webpage

Does it display correctly? Yell if it does not. When you press a button, the text should disappear

3.2.1.2  More about the timeline

Remember that the timeline’s value is an array, which can have multiple components.

Create a second text var, whose name is different from hello_trial, and whose text is also different. e.g., you could call the var goodbye_trial, and give it some text that says goodbye.

This second var should go between the first var and the call to jspsych.init()

Now, add the second var to the timeline: timeline: [hello_trial, goodbye_trial]. Knit the webpage, and see what happens. If all has gone well, your first text should disappear when a key is pressed, and the second text should appear. Then, the second text should disappear when a key is pressed again.

Your final webpage should look a little like this:

4 Create a complex study

Now, we will try to create a more complex study, based on the “simple rt experiment” that you tried earlier.

  1. Create a new directory in your Lab 3 folder called RTExpt. Download the lab .zip file again, and unzip it in this directory.
  2. Create a new RHTML file called RTExpt.rhtml and save it in the RTExpt directory.
  3. Make sure that the head of the file contains the following text, similarly to your previous experiment.
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
        <script src="jspsych-5.0.3/jspsych.js"></script>
        <script src="jspsych-5.0.3/plugins/jspsych-text.js"></script>
        <link href="jspsych-5.0.3/css/jspsych.css" rel="stylesheet" type="text/css"></link>
  1. Make sure you have <script> </script> tags under the <body> tags.

4.1  Display a welcome message

The first thing that participants usually see in an experiment is an introductory screen. We will use the jspsych text plugin to create an introductory welcome message to participants. This is very similar to what you were doing in the previous task.

var welcome_block = {
  type: "text",
  text: "Welcome to the experiment. Press any key to begin."
};

Next, we are going to display that message on the screen. To do this, we’ll use a slightly different technique from the last example, which gives you a little more flexibility and control.

In the previous example we displayed the text using the code below:

    jsPsych.init({
        timeline: [ hello_trial]
    })

Remember that the function jsPsych.init() had an argument (a key) called timeline, and that it took your text trial hello_trial as an argument.

This way of doing things can get hard to programme when you have many, many trials. You would need to write a piece of code that looks like this:

    jsPsych.init({
        timeline: [ hello_trial1, hello_trial2, hello_trial3, hello_trial4, hello_trial5, hello_trial6, hello_trial7, hello_trial8, hello_trial9, hello_trial10,hello_trial11, hello_trial12, hello_trial13, hello_trial13, hello_trial14, hello_trial15,hello_trial16....hello_trial180...]
    })

The code above is hard to read and understand. Don’t do it!

Instead, what we will do is create a new variable in which we pre-specify all the different trials, and then we’ll pass that variable as the argument of timeline.

var trial_structure = [];
trial_structure.push(welcome_block);

Some of the operations here should be familiar. Remember that var creates a new variable. [] says that this new variable is an array.

But the last operation, trial_structure.push(welcome_block);, is new. In this operation, we are taking the array trial_structure and we are “pushing”, i.e., adding, the variable welcome_block to that array.

If you do this correctly, then the value of the variable trial_structure should now be [welcome_block]. Shout if this does not make sense!

Now, you can use trial_structure as the timeline argument for jsPsych.init():

jsPsych.init({
  timeline: trial_structure
});

Check that your experiment knits and runs.

4.2 Make an Instructions screen

Now, we will create a slightly more complex piece of text that displays the instructions

var instructions_block = {
  type: "text",
  text: "<p>In this experiment, a circle will appear in the center " +
      "of the screen.</p><p>If the circle is <strong>blue</strong>, " +
      "press the letter F on the keyboard as fast as you can.</p>" +
      "<p>If the circle is <strong>orange</strong>, do not press " +
      "any key.</p>" +
      "<div class='left center-content'><img src='jspsych-5.0.3/tests&examples/img/blue.png'></img>" +
      "<p class='small'><strong>Press the F key</strong></p></div>" +
      "<div class='right center-content'><img src='jspsych-5.0.3/tests&examples/img/orange.png'></img>" +
      "<p class='small'><strong>Do not press a key</strong></p></div>" +
      "<p>Press any key to begin.</p>"
};

Place this text below the bit of code that creates welcome_block.

Here, we are creating the same type of object as before, i.e., a text object, but we are giving it some extra formatting using HTML tags. The value of the text key is still a line of text; but it is a line of text that contains HTML formatting. The HTML tags look a little like RMarkdown tags. For instance, where RMarkdown uses **two asterisks** to indicate boldface, HTML uses <strong></strong>.

We use the little + signs to glue the lines of text together into one long line of text (else each new line would be a different piece of text, and the text key can only taken one line as its value; Shout if you don’t understand this).

You can also see that we are displaying images in this bit of HTML:

<div class='left center-content'><img src='jspsych-5.0.3/tests&examples/img/blue.png'></img>"  +
"<p class='small'><strong>Press the F key</strong></p></div>" +
  • The <div> tag indicates that we are working on one section of the screen, called class='left center-content'>.
  • The tag <img src='jspsych-5.0.3/tests&examples/img/blue.png'></img> says that we are including an image, and src points to where on your computer that image should be (in folder jspsych-5.0.3 > tests&examples > img).

As an exercise, let’s play with the image paths. Create a new folder within the folder RTExpt called img. Copy the images blue.png and orange.png into that folder from their previous location (see the src for a hint if you don’t know where that is). Edit the src for each of the images above to point to the new location of the figures.

Now, push your instructions_block to the trial_structure array:

trial_structure.push(instructions_block);

4.3 Create a trial

In each trial of this experiment, participants will be shown a circle and they will then either press a key if the circle is blue, or withold a response if the circle is orange.

Creating a trial in JSPsych is very similar to creating a text screen, except that we use a slightly more complex plugin in order to make our stimulus timing more precise.

We will use a plugin called jspsych-single-stim.js. Load it as before, up in the <head> of your website: <script src="jspsych-5.0.3/plugins/jspsych-single-stim.js"></script>

Now, add some code down in the <script> section of your file to create two single-stim trials:

var blue_trial = {
  type: 'single-stim',
  stimulus: 'img/blue.png',
  choices: ['F'],
  timing_response: 1500
};

var orange_trial = {
  type: 'single-stim',
  stimulus: 'img/orange.png',
  choices: ['F'],
  timing_response: 1500
}

Here, we’ve created a variable called blue_trial of type single_stim which shows the blue.png as its stimulus, and allows participants to respond only by pressing the f key on their keyboard. The participants have 1500ms to respond to this trial. The same is also true for orange_trial, except that the image is different.

Now, add these two trials to your timeline

trial_structure.push(blue_trial, orange_trial);

4.3.1 Create a nested trial

You should now have a somewhat functional experiment, which shows an image on each trial and takes an F key as a response; trials end after 1.5s if the participant hasn’t responded.

This is pretty good! But what if we wanted to have loads and loads of different trials? We’d have to create loads and loads of new vars, which is inefficient.

JSPsych allows us to overcome this inefficiency by using nesting

var test_block = {
  type: 'single-stim',
  choices: ['F'],
  timeline: [
    {stimulus: 'img/blue.png'},
    {stimulus: 'img/orange.png'}
  ],
  timing_response: 1500

}

The var test_block is exactly the same as the previous trials, except that it has a timeline key:value pair. Here, the timeline takes an array [] that includes details of two stimuli:

 timeline: [
    {stimulus: 'img/blue.png'},
    {stimulus: 'img/orange.png'}
  ]

JSPsych is clever enough to realise that, when you write this, what you want is to create two trials that are identical except that they have different stimuli.

Now, delete the parts of your experiment that created and pushed blue_trial and orange_trial, and replace them with var test_block.

4.3.2  Generate lots of trials in random order

We can modify test_block so that it shows trials in random order, using a simple Key:Value pair. Insert randomize_order: true, within test_block and see if it works (try and figure out whey if it does not work; shout if you can’t do that quickly).

Now, we are going to modify test_block so that it has 10 trials rather than two. To do this, we are going re-use a trick from Section 4.1, where we created a variable for the timeline. Remember that currently test_block is structured as so:

var test_block = {
  type: 'single-stim',
  choices: ['F'],
  randomize_order: true,
  timeline: [
    {stimulus: 'img/blue.png'},
    {stimulus: 'img/orange.png'}
  ]
}

We are going to create a new variable that has twenty different trials in, and then plug that variable in to test_block so it looks as follows:

var test_block = {
  type: 'single-stim',
  choices: ['F'],
  randomize_order: true,
  timeline: all_trials
}

First, create a new variable called test_stimuli, that contains the values from the timeline on test_block:

var test_stimuli = [
  {stimulus: 'img/blue.png'},
  {stimulus: 'img/orange.png'}
];

Now, we will use a function called jsPsych.randomization.repeat() to create 5 copies of test_stimuli (i.e., 10 trials total) in random order:

var all_trials = jsPsych.randomization.repeat(test_stimuli, 5);

Then, we will plug all_trials into test_block:

var test_block = {
  type: 'single-stim',
  choices: ['F'],
  randomize_order: true,
  timeline: all_trials
}

If you like, you can take out randomize_order: true, now, because the use of jsPsych.randomization.repeat() means that our stimuli are already in random order.

Hopefully you have already built the new test_block by now, but if not go ahead. Remember to think of the order in which variables should be created. First you create test_stimuli, then you randomize it, then you create your block of test trials.

4.4  Gathering data

JSPsych automatically records most of the important data that you collect, such as the time it takes participants to respond to a stimulus, or what key they press. You can see the data that is automatically collected by adding the following Key: Value pair to the function JSPsych.init().

  on_finish: function() {
      jsPsych.data.displayData();
    }

This bit of code should be pasted onto a new line below the Timeline: Key:Value pair, and you should ensure that there is a comma at the end of the Timeline Key:Value pair.

When you run your RHTML file, you should now produce a list of all the data collected on each screen of the experiment (with key_press indicating the numeric code for the key; -1 is no response).

4.5  Augmenting data

From this new data file, you can see the responses for each stimulus; e.g., you can see what happens on trials where the stimulus was blue.png and orange.png.

It would be helpful to include additional information in this datafile, such as whether each trial was a Go trial or a No-Go trial. This is quite simple to do by augmenting the files that create the test trial.

For instance, you should edit the code for test_stimuli

var test_stimuli = [
  {stimulus: 'img/blue.png'},
  {stimulus: 'img/orange.png'}
];

And augment it to include information on the trial type

var test_stimuli = [
  {
    stimulus: "img/blue.png",
    data: { 
      response: 'go' 
      }
  },
  {
    stimulus: "img/orange.png",
    data: { 
      response: 'no-go' 
      }
  }
];

What we have done here is include an additional Key:Value pair. The Key is called data, and the value is another key set of key value pairs, which are enclosed in curly braces {}. This new set of Key:Value pairs is included in the datafile (see this by looking at the datafile that is now created).

5 Congrats & Advanced stuff

Congrats! By now, you should have created a working Go/No-Go experiment. It isn’t quite finished yet (the data doesn’t really save for example), but we’ll work on that in the future.

If you were able to do this very quickly, then you can also augment your experiment with some additional fancy stuff, by following the rest of Josh de Leeuw’s tutorial here.