To list the available tasks in the jsPsychMaker R package in you console, you can use the list_available_tasks() function. If you don’t have the jsPsychMaker package, install it first.
If you use the app, you will need to copy the generated config.js file to your protocol folder. The Shiny app can also help you create a parametrized consent form (see the Consent tab).
3.2.1 Main parameters
pid = 999;: Number of protocol
online = true;: true if the protocol runs in a server, false if it runs locally
max_participants = 3;: If you have between participants conditions (participants are assigned to only one of a number of conditions), this is the max number of participants per condition
random_id = false;: true if you want to assign a random id to participants, false if the participant needs to input an id
max_time = "24:00:00";: Maximum time to complete the protocol (HH:MM:SS; Hours:Minutes:Seconds)
accept_discarded = true;: If a participant is discarded (i.e. exceeded the max_time), should we allow them to continue, given there are available slots?
debug_mode = false;: When testing the protocol:
shows DEBUG messages
creates the DB tables if they don’t exist
Avoids randomization (e.g. order of items) so the jsPsychMonkeys can have a reproducible behavior
language = "English"; Language to use for the protocol. Either Spanish or English
3.2.2 Order of tasks
first_tasks = ['Consent'];// The protocol will start with these tasks in sequential orderlast_tasks = ['Goodbye'];// Last block of tasks presented (in sequential order)
Create as many blocks as needed:
randomly_ordered_tasks_1 = ['TASK1','TASK2'];// Block of tasks in random orderrandomly_ordered_tasks_2 = ['TASK3'];// Block of tasks in random ordersecuentially_ordered_tasks_1 = ['TASK5','TASK4'];// Block of tasks in sequential order
The final array of tasks can be build combining the above blocks. The order of the tasks in the arrays starting with “random” will be randomized.
jsPsychR will randomize participants to the different conditions keeping the unbalance between conditions to the minimum possible.
3.3 online-offline protocols
jsPsych uses standard web technologies (HTML, CSS y Javascript), so that protocols should run in any modern browser (updated, please). We recommend Google Chrome just because our test suite runs with Google Chrome, so we will catch its specific issues earlier.
3.3.1 Offline
If you want to run a protocol locally (on your computer, on a lab computer), you need to:
set online = false; in the config.js file
double click index.html
jsPsychR will use IndexedDB to store the participants’ progress and balance between conditions. The output csv files will be Downloaded to the Download folder of the computer where the protocol runs.
3.3.1.1 CORS ERRORS
If any of the tasks imports an html file, the Offline protocol will give a CORS error.
There are ways to disable web security in your browser, but it MUST only be done if your experiment computer runs offline, otherwise you will be exposed to very bad things.
Tu run a protocol online, set online = true; in the config.js file. You will need a couple more things:
MySQL running in your server
A file .secrets_mysql.php with the content below
Define the route to .secrets_mysql.php in controllers/php/mysql.php
require_once '../../.secrets_mysql.php';
THIS FILE MUST NOT BE PUBLICLY VISIBLE FROM THE BROWSER
Upload the files to the server :)
<?php/* DO NOT UPLOAD TO PUBLIC REPO */ $servername ="127.0.0.1"; $username ="USERNAME OF THE DATABASE"; $password ="PASSWORD OF THE DATABASE"; $dbname ="NAME OF THE DB";?>
jsPsychR will use MySQL to store the participants’ progress and balance between conditions. The output csv files will be Downloaded in the .data/ folder inside the protocol folder in the server.
Before launching the final experiment, make sure you start with a clean slate! That can be summarized in 3 simple steps:
Check the configuration for you experiment (config.js) and make sure all is well. Some of the critical bits are:
pid =999;// SHOULD have your project ID!online =true;// true is goodmax_participants =100;// Max participants per contition [number]max_time ="24:00:00";// Max time to complete the protocol [HH:MM:SS]debug_mode =false;// SHOULD be false
Check that the .data/ folder for your protocol is empty in the server. You will likely have remains of the piloting and Monkeys.
Clean up the MySQL data associated to your protocol.
SET @PID =999; // HERE YOUR PROTOCOL ID!deletefrom experimental_condition where id_protocol=@PID;deletefromuserwhere id_protocol=@PID;deletefrom user_condition where id_protocol=@PID;deletefrom user_task where id_protocol=@PID;deletefrom task where id_protocol=@PID;deletefrom protocol where id_protocol=@PID;
You will most likely need help from the server admin to perform these steps.
3.4 Language
We started implementing the basic blocks to be able to switch a protocol’s language from Spanish to English with the parameter language in the config.js file.
This will change the protocol’s hardwired messages (see config_messages.js), and will use the desired version of the task, if available. So far we only prepared a handful of tasks in multiple languages.
An example of a multilingual task would be Consent.js.
We have a Translations block:
// Translations --------------------------------------------------------------switch (language) {case"Spanish": Consent_000 = ['<p><left><b><big>Consentimiento informado</big></b><br /></p>']; Consent_001_choices = ['acepto participar','rechazo participar']; Consent_001_end ='Gracias por tu tiempo. Puedes cerrar esta página.';break;case"English": Consent_000 = ['<p><left><b><big>Informed consent</big></b><br /></p>']; Consent_001_choices = ['I agree to participate','I reject to participate']; Consent_001_end ='Thanks for your time. You can close this page.';break;}
And then a Task block, where the logic of the experiment is unique, and we use the variables created in the Translation block for the things the users will see in their screens:
Remember to place an if (debug_mode === false) before the randomization of the item order so when running in debug_mode, the items are not randomized. This is important so the behaviour of the jsPsychMonkeys is reproducible:
if (debug_mode ===false) NAMETASK = jsPsych.randomization.repeat(NAMETASK,1);
3.7 Technical aspects
We currently use jsPsych 6.3 as the default, and started to implement the last stable jsPsych (7.3) recently. To choose a version for the protocols you create, use the parameter jsPsych_version. For example, jsPsych_version = 7. Things are not fully tested in the v7 so, use with care.
The trialid identifies the trial, and the procedure makes possible to find that trial so participants can continue the tasks where they left, know when participants finished the tasks, etc. This is done in MySQL if online, IndexedDB if offline.
When running online tasks with between-participants variables, the system that balances conditions can change between-participants condition after the participants accept the consent form. If any of the items is not in a timeline (e.g. Instructions) and stores the condition_between, it may not be up to date. See Bayesian39 task for an example.
trialid’s need to have a standardized structure, which generally conforms with NameTask_3DigitNumber. When using conditional items the structure can be a bit more complex, but not much. We use the following rules to check for non-complying trialid’s:
^[a-zA-Z0-9]{1,100}_[0-9]{2,3}$ -> `NameTask_2or3DigitNumber`, for example `BNT_001`
^[a-zA-Z0-9]{1,100}_[0-9]{2,3}_[0-9]{1,3}$ -> `NameTask_2or3DigitNumber_1to3DigitsSuffix`, for example `BNT_002_1`
^[a-zA-Z0-9]{1,100}_[0-9]{2,3}_if$ -> `NameTask_2or3DigitNumber`, for example `BNT_002_if`
^[a-zA-Z0-9]{1,100}_[0-9]{2,3}_[0-9]{1,3}_if$ -> `NameTask_2or3DigitNumber`, for example `BNT_002_1_if`
if (debug_mode =='false') NameOfTask = jsPsych.randomization.repeat(NameOfTask,1);NameOfTask.unshift(instruction_screen_experiment);questions.push.apply(questions, NameOfTask)questions.push({type:'call-function',data: {trialid:'NameOfTask_000',procedure:'NameOfTask'},func:function(){if (online) {var data = jsPsych.data.get().filter({procedure:'NameOfTask'}).csv(); } else {var data = jsPsych.data.get().filter({procedure:'NameOfTask'}).json(); }saveData(data, online,'NameOfTask'); }});
3.7.3 Conditional questions
var question001 = {type:'survey-multi-choice-vertical',questions: [{prompt:'<div class="justified">¿Usted se ha vacunado contra el coronavirus / covid-19?</div>',options: [' Si',' No'],required:true,random_options:false,horizontal:false}],data: {trialid:'PVC_001',procedure:'PVC'}};PVC.push(question001);var question001_1 = {type:'survey-multi-choice-vertical',questions: [{prompt:'<div class="justified">¿Usted se va a vacunar contra el coronavirus covid-19?</div>',options: [' Si',' No',' No estoy seguro'],required:true,random_options:false,horizontal:false}],data: {trialid:'PVC_001_1',procedure:'PVC'}};var if_question001_1 = {timeline: [question001_1],data: {trialid:'PVC_001_1_if',procedure:'PVC'},conditional_function:function(){let data = (JSON.parse((jsPsych.data.get().values().find(x => x.trialid==='PVC_001'))['response'])['Q0']).trim();if((data) =='No'){returntrue; } else {returnfalse; } }};PVC.push(if_question001_1);
3.8 Common ERRORS
If you get the following error in the console: Uncaught TypeError: Cannot read properties of undefined (reading 'procedure')
Run this in the console:
for (var i =0; i < questions.length; i++) {console.log(i + questions[i].data["procedure"])}
It will stop in one of the items. Go to the console, check the array questions and go to the number that failed.
When you know the task and item that fails, you probably need to add: