; PSY 1903
PSY 1903 Programming for Psychologists

Results

To access the results of our experiment, we can add the on_start parameter to our debrief trial. This parameter accepts a function that will be executed when the trial is started.

Within this function, we can use jsPsych’s data.get().csv() method to retrieve our experiment results in CSV (Comma Separated Value) format and output those results to the console.

Updated debrief:

let debriefTrial = {
    type: jsPsychHtmlKeyboardResponse,
    stimulus: `
    <h1>Thank you for participating!</h1> 
    <p>You can close this tab.</p>
    `,
    choices: ['NO_KEYS'],
    // ⭐ NEW ⭐
    on_start: function () {
        let data = jsPsych.data.get().csv();
        console.log(data);
    }
};

When you complete the experiment, you should see the following results output to the console:

"rt","stimulus","response","trial_type","trial_index","plugin_version","time_elapsed"
"563","
    <h1>Welcome to the Lexical Decision Task</h1>
    <p>You are about to see a series of characters.</p>
    <p>Press F if the characters make up a word.</p>
    <p>Press J if the characters do not make up a word.</p>
    <p>Press SPACE to begin</p>
    "," ","html-keyboard-response","0","2.0.0","566"
"1471","<h1>mub</h1>","j","html-keyboard-response","1","2.0.0","2038"
"631","<h1>cat</h1>","f","html-keyboard-response","2","2.0.0","2670"
"715","<h1>pin</h1>","f","html-keyboard-response","3","2.0.0","3387"
"350","<h1>jgb</h1>","j","html-keyboard-response","4","2.0.0","3738"

Understanding the results

Each row in the results data represents one of the trials of our experiment. This includes the welcome trial and all 4 of our condition trials. The debrief trial is not included because our data output code was executed at the start of the debrief trial, so no data was yet available for it.

From the header of the CSV data, we see we have the following attributes for each trial:

  • "rt" Response time - How long it took the participant to respond to the given trial
  • "stimulus" What stimulus was displayed
  • "response" The participant’s response
  • "trial_type" What plugin was used
  • "trial_index" The index position of the trial within the timeline
  • "plugin_version" The version of the plugin that was used
  • "time_elapsed" How much time has elapsed since the start of the experiment

Filtering the results

We don’t really need the details of our welcome trial, so let’s come up with a way to filter our results to just our condition trials.

To do this, we will add a data property to our condition trials. The data property allows us to attach our own custom data to any trial. For our purposes, we will create a data property called collect and set it to true, representing that we wish to collect the data from this trial.

for (let condition of conditions) {
    let trial = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: `<h1>${condition.characters}</h1>`,
        choices: ['f', 'j'],
        // ⭐ NEW  ⭐
        data: {
            collect: true, // Assign a custom property `collect` so we can filter our results data
        },
    };

    timeline.push(trial);
}

Then, we’ll update our on_start function in the debrief trial to use data’s filter method to filter the results to just trials where the collect property is true.

on_start: function () {
    let data = jsPsych.data
        .get()
        .filter({ collect: true }) // ⭐ NEW - Filter our results to only trials where `collect` is `true`
        .csv();
    console.log(data);
}

Now our results no longer include the welcome trial:

"collect","rt","stimulus","response","trial_type","trial_index","plugin_version","time_elapsed"
"true","705","<h1>cat</h1>","f","html-keyboard-response","1","2.0.0","1187"
"true","617","<h1>pin</h1>","f","html-keyboard-response","2","2.0.0","1805"
"true","311","<h1>jgb</h1>","j","html-keyboard-response","3","2.0.0","2117"
"true","309","<h1>mub</h1>","j","html-keyboard-response","4","2.0.0","2426"

Ignoring data

To further simplify our results, we can use data’s ignore method to filter out attributes we don’t need. The attributes we will ignore are: , 'trial_type', 'trial_index', 'plugin_version', 'collect'.

// Filter and retrieve results as CSV data
let results = jsPsych.data
    .get()
    .filter({ collect: true })
    .ignore(['trial_type', 'trial_index', 'plugin_version', 'collect'])
    .csv();

Results:

"rt","stimulus","response","time_elapsed"
"1122","<h1>cat</h1>","f","1622"
"770","<h1>mub</h1>","j","2394"
"550","<h1>jgb</h1>","j","2944"
"289","<h1>pin</h1>","f","3235"

Capturing characters shown

In our results output, we see what characters were shown because the stimulus property is collected, but it’s cluttered with HTML code, e.g.: "<h1>cat</h1>".

To clean this up, let’s add a custom property to our data that will record just the characters shown (no HTML):

for (let condition of conditions) {
    let conditionTrial = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: `<h1>${condition.characters}</h1>`,
        choices: ['f', 'j'],
        data: {
            collect: true,
            // ⭐ NEW - Record what characters were shown
            characters: condition.characters,
        }
    }
    timeline.push(conditionTrial);
}

Then, we can update the ignore method to exclude the stimulus property:

.ignore(['stimulus', 'trial_type', 'trial_index', 'plugin_version', 'collect'])

Results:

"characters","rt","response","time_elapsed"
"cat","892","f","1427"
"jgb","865","j","2294"
"mub","1271","j","3566"
"pin","413","f","3979"

Determining outcomes

The last tweak we should make to our results data is to include information on whether or not the participant responded correctly - that is - did they press "f" for words and "j" for pseudo-words.

To do this, we will add an on_finish function to our condition trials that will examine the response input by the participant and determine a Boolean value as to whether it was correct.

for (let condition of conditions) {
    let conditionTrial = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: `<h1>${condition.characters}</h1>`,
        choices: ['f', 'j'],
        data: {
            collect: true,
        },
        // ⭐ NEW ⭐
        on_finish: function (data) {
            if (data.response == 'f' && condition.isWord == true) {
                data.correct = true;
            } else if (data.response == 'j' && !condition.isWord == false) {
                data.correct = true;
            } else {
                data.correct = false;
            }
        }
    };
    timeline.push(conditionTrial);
}

Observe how in the above code, the on_finish function has a data parameter which gives us access to the data attributes of the given trial. So similarly to how we defined data.collect, we can define a parameter, data.correct which will be made available in our results:

"characters","rt","response","time_elapsed","correct"
"mub","1307","j","2636","true"
"pin","612","f","3250","true"
"cat","574","f","3825","true"
"jgb","372","j","4199","true"

Next...

Continuing on, let’s learn how to tackle more complex experiments that might be divided into multiple blocks...

Code so far

let jsPsych = initJsPsych();

let timeline = [];

// Welcome
let welcomeTrial = {
    type: jsPsychHtmlKeyboardResponse,
    stimulus: `
    <h1>Welcome to the Lexical Decision Task!</h1> 
    <p>You are about to see a series of characters.</p>
    <p>If the characters make up a word, press the F key.</p>
    <p>If the characters do not make up a word, press the J key.</p>
    <p>Press SPACE to begin.</p>
    `,
    choices: [' ']
};
timeline.push(welcomeTrial);

// Show word or pseudo word (on repeat)
// Create an array of conditions
let conditions = [
    { characters: 'cat', isWord: true },
    { characters: 'pin', isWord: true },
    { characters: 'jgb', isWord: false },
    { characters: 'mub', isWord: false },
];

// Shuffle the conditions
conditions = jsPsych.randomization.repeat(conditions, 1);

for (let condition of conditions) {
    let conditionTrial = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: `<h1>${condition.characters}</h1>`,
        choices: ['f', 'j'],
        data: {
            collect: true,
            characters: condition.characters,
        },
        on_finish: function (data) {
            if (data.response == 'f' && condition.isWord == true) {
                data.correct = true;
            } else if (data.response == 'j' && condition.isWord == false) {
                data.correct = true;
            } else {
                data.correct = false;
            }
        }
    }
    timeline.push(conditionTrial);
}

// Debrief
let debriefTrial = {
    type: jsPsychHtmlKeyboardResponse,
    stimulus: `
    <h1>Thank you!</h1>
    <p>You can now close this tab</p>
    `,
    choices: ['NO KEYS'],
    on_start: function () {
        let data = jsPsych.data
            .get()
            .filter({ collect: true })
            .ignore(['stimulus', 'trial_type', 'trial_index', 'plugin_version', 'collect'])
            .csv();
        console.log(data);
    }
}
timeline.push(debriefTrial);

jsPsych.run(timeline);