A very introductory guide to jsPsych

“How to run online experiment from scratch”

Introduction

This document is a quick guide to start prototyping online experiments. Targeted at people with some programming knowledge but little to no experience with programming for the web.

The learning curve may feel steep and the reason for that is that the web is a mess of tools stacked onto one another. Luckily for us, Josh de Leeuw and other folks have created jsPsych de Leeuw, J. R. (2015). jsPsych: A JavaScript library for creating behavioral experiments in a web browser. Behavior Research Methods, 47(1), 1-12. doi:10.3758/s13428-014-0458-y.

which simplifies the process of designing an online behavioral experiment a lot. I can only emphasize that you go to the main website for documentation, it’s exhaustive and well written, but it assumes some knowledge of the web ecosystem. The goal of this document is rather to provide the high level, overview of what it even means to program for the web.

The web, or what you’ll need to start to learn

Here’s how the web works, roughly: a bunch of files sit on a server computer, somewhere you’ve likely never seen. When someone reaches for an web address, some magic process happens which connects that person’s computer and the server and things start to unfold. There are different type of files, and you’ll need to learn a few of them in order to understand what’s happening next. Here is a brief description of possible cases:

In order to write complex experiments you’ll only need to learn more about JavaScript. But since all file types are involved, you’ll need a superficial understanding of what’s going on with the others.

Case study

In order to get into more details you can find here the skeleton of an experiment that has most of the useful ingredients, which you can try it here. Let’s break it downWhat happens when someone connects to the server to get the experiement? schema describing the successive steps of an experiment after a participant connectsA rough description of the five steps that take place whenever an experiment runs. While the computation is done on the participant computer, a server is required to send the files and retrieve the data.

skeleton
├── index.php
├── done.html
├── exp.html
├── external-consent.html
├── script.js
├── instruction-text.js
└── resources
    ├── my-style.css
    ├── save-data.php
    ├── save-data.js
    └── jspsych
        ├── jspsych.js
        ├── license.txt
        ├── css
        │   └── jspsych.css
        ├── my-plugins
        │   └── jspsych-survey-dropdown.js
        └── plugins
            ├── ...
            └── template
                └── jspsych-plugin-template.js

You can see two php files, three hmtl files, two css files, and a bunch of js (JavaScript) files.

index.php

When I share an experiment to the world, I give them a link to that page. Since it runs computations on the server it can compute how many participants took the task so far and act accordingly, or maybe it can randomly assign participants to various conditions.

What my index.php usually does is to check whether the experiment is still running (for example by checking the number of participants who already took it), and then either sends the browser done.html if no more participants are required, or serves exp.html otherwise. done.html is pretty straightforward, you can look at it to see a minimal HTML file but I won’t say much about it.

exp.html

On the other hand exp.html is a pretty uncommon HTML file. It looks like this:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>experiment</title>
    <!-- js stuff -->
    <script src="resources/jspsych/jspsych.js"></script>
    <script src="..."></script>
    ...
    <!-- css stuff -->
    <link rel="stylesheet" href="resources/jspsych/css/jspsych.css"></link>
    <link rel="stylesheet" href="..."></link>
    ...
    <link rel="stylesheet" href="script.js"></link>
  </head>
</html>

So really it loads a bunch of external files and that’s it. The reason for that is that in our case, jsPsych is doing the heavy lifting: it dynamically creates a page for you, sets up a bunch of variables and timers, and allow you to handle the page entirely from the js side — you’ll note that indeed the first thing it loads is the jspsych root file jspsych.js. Order matters, here: earlier file get loaded and executed first. Typically the order is: main jspsych.js file, then plugins, then style, then your main script, that I call in the exemple script.js

script.js

A typical jsPsych experiment script looks like this:

// Define several blocks
var block1 = {
    type:'...',
    ...
};

var block2 = {
    type:'...',
    ...
};

// Start the experiment whene everything is ready
document.addEventListener("DOMContentLoaded", function(event) {
    jsPsych.init({
        timeline: [block1, block2],
        on_finish: SaveData(...);
        }
    });
});

In this file you define the various blocks that are the different parts of your experiment, and you ask jsPsych to start the experiment for you.

What you put in type:'...' depends on the jsPsych plugin you want to run. Let’s say you want to write a bloc containing the instructions: you’re in luck, there’s a plugin for that whose doc is here. So you check out the documentation and you come up with the following block, for example:

var instruction_text_p1 = "This is the text on the first page of instructions"
var instruction_text_p2 = "This is the text on the second page of instructions"
var instructions = {
    type:'instructions',
    show_clickable_nav: true,
    pages: [instruction_text_p1, instruction_text_p2]
};

Since text are quickly long, and you may want to avoid cluttering your files with too much text, you can keep the block definition in the script.js file but put the text in a dedicated file: this is the purpose of my instruction-text.js that only contains the definition of the variables setting the text for the instructions. The cost of breaking things into smaller files is often vastly compensated by the fact that the cleaner the code, the easier it is to check, share, read, debug, etc. Note that if you do so, you need to modify your html file to include this new js file before you use the variables: remember, files are loaded and executed in the order in which they appear in the html file.

This means that you need to change your exp.html accordingly to source the new file before the main one, in a manner similar to this:

[...]
    <script src="instruction-text.js"></script>
    <script src="script.js"></script>
[...]

my-style.css

While you may be perfectly happy with the default style that ships with jsPsych, one often need to finely tune some things to match the expectations of the experiments. This is done in the css file. Typically a CSS file looks like this:

p {
    text-align:justify;
    font-size: 12px;
    max-width: 800px;
}

#consent {
    color: #4dac26;
    background-color: red;
}

.consent {
    color: #4dac26;
    background-color: red;
}

[...]

Which the browser roughly understands as this:

  • Every element whose nature is to be a paragraph should have text justified, of a given font size and no more than this many pixels wide.
  • Every element that’s tagged to be of type consent should be written in green on red (please don’t do .that).
  • The element uniquely identified as consent should be written in green on red.

The p refers to an html element <p>This is a paragraph</p>, the #consent refers to an html element whose class is consent such as <div class='consent'>This is an online [...]</div> and finally the .consent refers to an html element uniquely identified as consent, such as <input id='consent' type='button" value="I consent">

Mozilla publishes documentation about html, css and javascript. This is a good entry point, and in general when you look for the documentation of a javascript function or a css style, e.g. how to handle clicks, their website is helpful

What about the other files?

Retrieving the data

The question of how to save data is tricky and requires some thinking. When the experiment ends, the data is on participants computer, as JavaScript runs on their computer and not on yours. There are ways in the protocol to handle this, and I don’t want to expand too much on them, but basically the skeleton I put here has a pair of files that are designed to communicate the data from one computer to the other: save-data.js and save-data.php. If you run things on a computer you control, jsPsych has a thing meant to help you.

At NeuroSpin, we’ve setup a system to receive the files from the participants computer to an intranet-synced computer, more on the wiki at some point.

What about external-conent.html?

I chose to present the consent as a distinct page which was agreed upon. This is loaded through yet another plugin, external-html.

What is this license.txt file?

I didn’t write jsPsych, but those who did decided to publish it with a corresponding license to tell you what rights and duty you have with regard to the intellectual property of those who came up with the system. They mention it on the website here but it states pretty clearly that one can do a lot of thing with it provided:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

So, I can’t put the skeleton online without a copy of the license, which I happily do since it gives credit back to the original authors!

How to start playing with this

While most browsers will happily read files from your hard drive, it won’t really reproduce the behaviour you’d get online. Ultimately you’ll need a server on which you can put your files, a server that understands php files and has a public facing domain name such as blog.s-m.ac/using-jspsych, but while you’re just testing and learning it’s a bit overkill.

Installing a full fledged server on your computer can be tedious and even dangerous if you don’t know what you’re doing, but luckily for you there’s a simple thing you can do. It won’t handle the PHP files but will be enough for html and css and js: if you can access a console that has python available, you can go to the folder where your files are, and run either

python -m http.server 8181

Or

python3 -m http.server 8181

Which will create a bare bone server for you, that you can open in your browser by going to the address http://127.0.0.1:8181/.

Useful tricks up your sleeves

The browser is outsmarting you …

Browsers are not always your friend. They will do whatever they can to optimize things: loading time, computations, etc. In that process they will produce seemingly unexpected behavior.

The main one you will encounter is caching: when the browser reads an html files and sees a JavaScript file, it will first try to see if it already has it to avoid having to load it and to parse it again. For one’s typical browsing pattern, this is great: things are faster and consume less bandwidth. But when you write code it can be a pain, because you change your code and the browser ignores the changes! Each browser has its own shortcut to reload everything, these typically works: Ctrl+F5 or Ctrl+Maj+R

… but is such a great resource!

It provides awesome debugging/inspecting tools. You should notably learn to use the browser’s console: you open it by pressing Ctrl+Maj+i, or maybe F12. This is the browser giving you access to its internal representation of the code and the page. You can print the value of variables, see how your page is structured, etc.

And in particular, it gives you access to printf() debugging, the process by which one places print statements in the code to unfold the execution flow and investigate things. In the JavaScript world, the function is console.log(): it outputs its argument(s) to the console. If you open your console you should see the result of the following piece of code written in the page:

console.log("I'm a string");
console.log({foo: "bar", whoAmI: "An object"});
console.log(3*Math.sin(2));

Play around with the console, you can:

  • Modify elements
  • Print variables
  • Inspect page, the memory, etc.

Among other things, you can simulate other setups and browsers: phones, touchscreens, limited connections, etc. Explore! Have fun!

Beware of “framework” answers

javscript is a programming language not everyone was happy with (you’ll quickly learn why). It has gotten better over time, but people created “frameworks” around it to make some operations easier to deal with, and a notable one is jQuery. The reason I’m pointing this out is that jsPsych does not assume you use any of these, but stackoverflow.com often does.

Try to avoid jQurey answersjQuery introduced new syntax: if an answers seems to use a lot of $('...') it probably assumes you’re using jQuery, which typically tou’re not

, look for vanilla javascript, or generally be aware that this could be an issue.

From the skeleton to an experiment

From the skeleton, building a better experiment is just adding more blocks, with different plugins. jsPsych provides many plugins as well as a template to create your own and a comprehensive documentation. That being said, rolling on your own plugins will require delving quite a lot into how JavaScript works.

List of resources from MDN