Skip to content

Scripts

Experiments Surveys & forms

The script column injects custom JavaScript into a trial. It is aimed at researchers who are comfortable writing JavaScript, and it unlocks things the other columns cannot do on their own: running tallies across trials (error counts, scores), lists built from participants’ responses, custom completion codes, and arbitrary side effects.

This page covers defining your own variables and lists. For consuming values with %variable% placeholders in stimuli, feedback, and instructions, see Using Variables.

Each trial has exactly two execution moments:

  • Start stage: runs right before the trial’s screens are shown, for every displayed row of any type.
  • End stage: runs when the trial’s response is saved, but only for type = test rows and form pages. Practice, learn, and instruction rows never run the end stage, and CFPT trials do not either.

The whole cell is the start stage by default. A line beginning with end: splits the cell: everything before it is the start stage, everything after it is the end stage.

%attempts% += 1
end:
if(%correct% == 0){ %errors% = (%errors% || 0) + 1; }

Putting end: on the first line gives you an end stage only. Only the first end: split is used; code after a second end: line is silently dropped.

Any %name% in a script that is not one of the reserved names (response, RT, correct) becomes a session variable: it is shared by all scripts and persists across trials.

// Initialise
%score% = 0
// Update
%score% += %correct%
%repeats% += 1
%myText% = "Hello participant!"

Once set, the variable can be used as %score% or %myText% in the text of any subsequent row (stimuli, feedback, instructions, questions). Numbers with a fractional part are displayed with two decimals (2.5 renders as 2.50); whole numbers render without decimals.

A few things to watch:

  • Reserved placeholder names are case sensitive: %Response% silently becomes a session variable named Response instead of reading the recorded response.
  • Reserved names take an optional integer suffix: %response-1% is the response before the most recent one, and a positive suffix reads by spreadsheet row (%response2% is the first data row, because row 1 is the header; %response1% is always undefined).
  • Session variables referenced before any script assigns them are undefined, unless they share a name with a stimulus list (see below).
  • Session variables live only for the session. After every saved trial they are all written as key : value; pairs into the tallies results column, which is the persistent record.

Assigning to the session variable %completionCode% replaces the generated completion code on the end screen, provided the project has completion codes enabled and set to a random (not fixed) code:

type,content,script
instruction,Welcome,%completionCode% = 'AB' + Math.floor(Math.random()*90000 + 10000);

A session variable can hold a list, which makes the script column an alternative to uploading a CSV list file:

%animals% = ['dog', 'cat', 'bird', 'fish']

The list can then drive question generation (#animals#), response options (%animals%), and the other list features described in Generating Questions from Lists.

Reading a session variable that was never assigned but matches the name of an uploaded stimulus list returns a copy of that static list, so uploaded lists and script-defined lists are interchangeable from the consumer’s point of view.

Building lists dynamically with addToList()

Section titled “Building lists dynamically with addToList()”

addToList(%listName%, value) appends a value to a session list. If the list does not exist yet, it is seeded from the uploaded stimulus list of the same name, or starts empty. A common use is accumulating participants’ responses for later trials:

type,stim1,script
test,pick a word,"end:
addToList(%chosenWords%, %response%)"

Each trial’s response is appended to the chosenWords list. Because dynamic list expansion reads session lists, an addToList in an end stage can feed the stimuli or questions of later trials.

Rules that matter in practice:

  • Inside the arguments of addToList(...), every non-reserved %name% becomes the string literal "name". That is how the first argument names the list, but it applies to the value argument too: pass values as reserved placeholders (%response% and friends) or plain JavaScript expressions, never as %someUserVar% (that would append the literal string someUserVar).
  • String values are split on ; into multiple items.
  • Empty values are silently ignored: appending 0, an empty string, or an unset variable does nothing.
  • Keep each addToList(...) call on its own line, without a second call or trailing code on the same line.

A start stage script can call skipTrial() to immediately advance to the next trial. It does nothing on the last trial or after the end screen. Scripts do not otherwise participate in if/then branching, which is handled by the then column.

A common pattern is to repeat a trial until the participant answers correctly, up to a maximum number of attempts. Use script to track attempts and if/then to loop back:

labelstimscriptifthen
q1What is 2+2?%repeats% = 0
%repeats% += 1correct | %repeats% > 4+1;0

How it works:

  1. The first row initialises %repeats% to 0 and shows the question.
  2. The second row increments %repeats% by 1 each time it runs.
  3. The if condition checks whether the answer is correct or the participant has already tried more than 4 times.
  4. then = +1;0 means: if the condition is met skip to the next row (+1), otherwise jump back to the question row to try again.