An easy-to-use platform for EEG experimentation in the classroom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

experiment backend cleanup

- removed remnants of jspsych from the project
- migrated helper function

+12 -1277
-1
app/app.global.css
··· 4 4 */ 5 5 @import '~font-awesome/css/font-awesome.css'; 6 6 @import '~semantic-ui-css/semantic.min.css'; 7 - @import '~jspsych-react/css/jspsych.css'; 8 7 @import '~rc-slider/assets/index.css'; 9 8 @import '~react-toastify/dist/ReactToastify.css'; 10 9 @import './utils/labjs/lab.css';
+1 -3
app/components/CollectComponent/RunComponent.js
··· 9 9 import { injectMuseMarker } from '../../utils/eeg/muse'; 10 10 import { EXPERIMENTS, DEVICES } from '../../constants/constants'; 11 11 import { ExperimentWindow } from '../../utils/labjs'; 12 - import { checkFileExists } from '../../utils/filesystem/storage'; 13 - 14 - import { parseTimeline, instantiateTimeline, getImages } from '../../utils/jspsych/functions'; 12 + import { checkFileExists, getImages } from '../../utils/filesystem/storage'; 15 13 import { MainTimeline, Trial, ExperimentParameters } from '../../constants/interfaces'; 16 14 17 15 import { remote } from 'electron';
+1 -1
app/components/PreviewExperimentComponent.js
··· 4 4 import { ExperimentWindow } from '../utils/labjs'; 5 5 import styles from './styles/collect.css'; 6 6 7 - import { parseTimeline, instantiateTimeline, getImages } from '../utils/jspsych/functions'; 7 + import { getImages } from '../utils/filesystem/storage'; 8 8 import { MainTimeline, Trial, ExperimentParameters } from '../constants/interfaces'; 9 9 10 10 interface Props {
+1 -4
app/constants/interfaces.js
··· 7 7 // TODO: Write interfaces for device objects (Observables, Classes, etc) 8 8 9 9 // ------------------------------------------------------------------ 10 - // jsPsych 11 - 12 - // TODO: Write interface for jsPsych plugins 10 + // lab.js Experiment 13 11 14 12 export type ExperimentParameters = { 15 13 trialDuration: number, ··· 17 15 iti: number, 18 16 jitter: number, 19 17 sampleType: string, 20 - pluginName: string, 21 18 intro: string, 22 19 // Setting this to any prevents ridiculous flow runtime errors 23 20 showProgessBar: any,
+9
app/utils/filesystem/storage.js
··· 176 176 ); 177 177 }); 178 178 179 + // Returns an array of images that are used in a timeline for use in preloading 180 + export const getImages = (params: ExperimentParameters) => 181 + readdirSync(params.stimulus1.dir) 182 + .map((filename) => path.join(params.stimulus1.dir, filename)) 183 + .concat( 184 + readdirSync(params.stimulus2.dir).map((filename) => path.join(params.stimulus2.dir, filename)) 185 + ); 186 + 187 + 179 188 // ----------------------------------------------------------------------------------------------- 180 189 // Util 181 190
-202
app/utils/jspsych/functions.js
··· 1 - import { isNil } from 'lodash'; 2 - import { jsPsych } from 'jspsych-react'; 3 - import * as path from 'path'; 4 - import { readdirSync } from 'fs'; 5 - import { EXPERIMENTS } from '../../constants/constants'; 6 - import { buildOddballTimeline } from './timelines/oddball'; 7 - import { buildN170Timeline } from './timelines/n170'; 8 - import { buildSSVEPTimeline } from './timelines/ssvep'; 9 - import { buildStroopTimeline } from './timelines/stroop'; 10 - import { buildMultiTimeline } from './timelines/multi'; 11 - import { buildSearchTimeline } from './timelines/search'; 12 - import { buildCustomLine } from './timelines/custom'; 13 - 14 - import { MainTimeline, Trial, ExperimentParameters } from '../../constants/interfaces'; 15 - 16 - // loads a normalized timeline for the default experiments with specific callback fns 17 - export const loadTimeline = (paradigm: EXPERIMENTS) => { 18 - console.log('paradigm', paradigm); 19 - let timeline; 20 - switch (paradigm) { 21 - case EXPERIMENTS.P300: 22 - timeline = buildOddballTimeline(); 23 - break; 24 - 25 - case EXPERIMENTS.N170: 26 - timeline = buildN170Timeline(); 27 - break; 28 - 29 - case EXPERIMENTS.SSVEP: 30 - timeline = buildSSVEPTimeline(); 31 - break; 32 - 33 - case EXPERIMENTS.STROOP: 34 - timeline = buildStroopTimeline(); 35 - break; 36 - 37 - case EXPERIMENTS.MULTI: 38 - timeline = buildMultiTimeline(); 39 - break; 40 - 41 - case EXPERIMENTS.SEARCH: 42 - timeline = buildSearchTimeline(); 43 - break; 44 - 45 - case EXPERIMENTS.CUSTOM: 46 - timeline = buildN170Timeline(); 47 - break; 48 - 49 - default: 50 - timeline = buildN170Timeline(); 51 - break; 52 - } 53 - return timeline; 54 - }; 55 - 56 - // Converts a normalized timeline template into a classic jsPsych timeline array 57 - export const parseTimeline = ( 58 - params: ExperimentParameters, 59 - mainTimeline: MainTimeline, 60 - trials: { [string]: Trial }, 61 - timelines: {} 62 - ) => { 63 - const parsedTimelines = Object.assign( 64 - ...Object.entries(timelines).map(([id, timeline]) => ({ 65 - [id]: { 66 - ...timeline, 67 - timeline: [ 68 - { 69 - ...timeline.timeline[0], 70 - trial_duration: () => params.iti + Math.random() * params.jitter, 71 - }, 72 - { 73 - ...timeline.timeline[1], 74 - stimulus: jsPsych.timelineVariable('stimulusVar'), 75 - type: params.pluginName, 76 - trial_duration: params.trialDuration, 77 - choices: [params.stimulus1.response, params.stimulus2.response], 78 - event_title: jsPsych.timelineVariable('eventTitleVar'), 79 - correct_response: jsPsych.timelineVariable('responseVar'), 80 - }, 81 - ], 82 - sample: { 83 - type: params.sampleType, 84 - size: params.nbTrials, 85 - }, 86 - timeline_variables: readdirSync(params.stimulus1.dir) 87 - .map((filename) => ({ 88 - stimulusVar: path.join(params.stimulus1.dir, filename), 89 - eventTypeVar: params.stimulus1.type, 90 - eventTitleVar: params.stimulus1.title, 91 - responseVar: params.stimulus1.response, 92 - })) 93 - .concat( 94 - readdirSync(params.stimulus2.dir).map((filename) => ({ 95 - stimulusVar: path.join(params.stimulus2.dir, filename), 96 - eventTypeVar: params.stimulus2.type, 97 - eventTitleVar: params.stimulus2.title, 98 - responseVar: params.stimulus2.response, 99 - })) 100 - ), 101 - }, 102 - })) 103 - ); 104 - 105 - // Inserts param.intro as stimulus property of first trial 106 - const parsedTrials = Object.assign( 107 - ...Object.entries(trials).map(([id, trial]) => { 108 - if (id === mainTimeline[0]) { 109 - return { [id]: { ...trial, stimulus: params.intro } }; 110 - } 111 - return { [id]: trial }; 112 - }) 113 - ); 114 - 115 - // Combine trials and timelines into one object 116 - const jsPsychObject = { ...parsedTrials, ...parsedTimelines }; 117 - // Map through the mainTimeline, returning the appropriate trial or timeline based on id 118 - return mainTimeline.map((id) => jsPsychObject[id]); 119 - }; 120 - 121 - // Fills in on_load functions in jsPsych timeline array with callback functions 122 - // For events, looks for an existing eventType object in the trial to set a value for the eventCallback 123 - export const instantiateTimeline = ( 124 - timeline: Object, 125 - eventCallback: (?string) => void, 126 - startCallback: ?() => void = null, 127 - stopCallback: ?() => void = null, 128 - showProgessBar: ?boolean = false 129 - ) => 130 - timeline.map((jspsychObject, index) => { 131 - if (index === 0) { 132 - // intro 133 - return { ...jspsychObject, on_finish: startCallback }; 134 - } 135 - if (index === timeline.length - 1) { 136 - // end 137 - return { ...jspsychObject, on_load: stopCallback }; 138 - } 139 - if (!isNil(jspsychObject.timeline)) { 140 - const timelineWithCallback = jspsychObject.timeline.map((trial) => { 141 - if (trial.id === 'trial') { 142 - return { 143 - ...trial, 144 - on_start: () => { 145 - const eventType = jsPsych.timelineVariable('eventTypeVar')() || 0; 146 - eventCallback(jsPsych.timelineVariable('eventTypeVar')(), new Date().getTime()); 147 - }, 148 - on_finish: (data: any) => { 149 - data.key_press = jsPsych.pluginAPI.convertKeyCodeToKeyCharacter(data.key_press); 150 - data.expected_key_press = trial.correct_response(); 151 - data.event_title = trial.event_title(); 152 - if (data.key_press === data.expected_key_press) { 153 - data.correct = true; 154 - } else { 155 - data.correct = false; 156 - } 157 - if (showProgessBar) { 158 - jsPsych.setProgressBar( 159 - jsPsych.progress().current_trial_global / 2 / jspsychObject.sample.size 160 - ); 161 - } 162 - }, 163 - }; 164 - } 165 - return trial; 166 - }); 167 - return { ...jspsychObject, timeline: timelineWithCallback }; 168 - } 169 - return jspsychObject; 170 - }); 171 - 172 - // Gets the last set of behavioral (key press) data stored in jsPsych 173 - export const getBehaviouralData = () => { 174 - const rawData = jsPsych.data.get().values(); 175 - 176 - // Mutate rawData array to customize behavioural results output 177 - for (let index = 0; index < rawData.length; index++) { 178 - rawData[index] = { 179 - ...rawData[index], 180 - reaction_time: rawData[index].rt, // rename rt to reaction_time 181 - trial_index: rawData[index].trial_index / 2, // Remove fixations from trial index 182 - }; 183 - } 184 - rawData.shift(); // Remove first 'welcome' trial 185 - rawData.pop(); // Remove last 'thanks for participating' trial 186 - 187 - return jsPsych.data 188 - .get() 189 - .filterCustom((trial) => !trial.stimulus.includes('fixation')) // Remove inter trial data 190 - .ignore('rt') 191 - .ignore('internal_node_id') 192 - .ignore('trial_type') 193 - .csv(); 194 - }; 195 - 196 - // Returns an array of images that are used in a timeline for use in preloading 197 - export const getImages = (params: ExperimentParameters) => 198 - readdirSync(params.stimulus1.dir) 199 - .map((filename) => path.join(params.stimulus1.dir, filename)) 200 - .concat( 201 - readdirSync(params.stimulus2.dir).map((filename) => path.join(params.stimulus2.dir, filename)) 202 - );
-153
app/utils/jspsych/plugins/callback-html-display.js
··· 1 - /** 2 - * callback-html-display 3 - * Teon L Brooks 4 - * modified from Josh de Leeuw 5 - * 6 - * plugin for displaying a stimulus and getting a keyboard response 7 - * 8 - * documentation: docs.jspsych.org 9 - * 10 - **/ 11 - import { jsPsych } from 'jspsych-react'; 12 - 13 - var plugin = (function() { 14 - var plugin = {}; 15 - 16 - plugin.info = { 17 - name: 'html-keyboard-response', 18 - description: '', 19 - parameters: { 20 - stimulus: { 21 - type: jsPsych.plugins.parameterType.HTML_STRING, 22 - pretty_name: 'stimulus', 23 - default: undefined, 24 - description: 'The HTML string to be displayed', 25 - }, 26 - choices: { 27 - type: jsPsych.plugins.parameterType.KEYCODE, 28 - array: true, 29 - pretty_name: 'Choices', 30 - default: jsPsych.ALL_KEYS, 31 - description: 'The keys the subject is allowed to press to respond to the stimulus.', 32 - }, 33 - prompt: { 34 - type: jsPsych.plugins.parameterType.STRING, 35 - pretty_name: 'Prompt', 36 - default: '', 37 - description: 'Any content here will be displayed below the stimulus.', 38 - }, 39 - stimulus_duration: { 40 - type: jsPsych.plugins.parameterType.INT, 41 - pretty_name: 'Stimulus duration', 42 - default: -1, 43 - description: 'How long to hide the stimulus.', 44 - }, 45 - trial_duration: { 46 - type: jsPsych.plugins.parameterType.INT, 47 - pretty_name: 'Trial duration', 48 - default: -1, 49 - description: 'How long to show trial before it ends.', 50 - }, 51 - response_ends_trial: { 52 - type: jsPsych.plugins.parameterType.BOOL, 53 - pretty_name: 'Response ends trial', 54 - default: true, 55 - description: 'If true, trial will end when subject makes a response.', 56 - }, 57 - }, 58 - }; 59 - 60 - plugin.trial = function(display_element, trial) { 61 - var new_html = '<div id="jspsych-html-keyboard-response-stimulus">' + trial.stimulus + '</div>'; 62 - 63 - // add option for on_start parameter in the experiment 64 - if (typeof trial.on_start === 'function') { 65 - trial.on_start.call(); 66 - } else if (typeof trial.on_start !== 'undefined') { 67 - trial.data['on_start'] = trial.on_start; 68 - } 69 - 70 - // add prompt 71 - new_html += trial.prompt; 72 - 73 - // draw 74 - display_element.innerHTML = new_html; 75 - 76 - // store response 77 - var response = { 78 - rt: -1, 79 - key: -1, 80 - }; 81 - 82 - // function to end trial when it is time 83 - var end_trial = function() { 84 - // kill any remaining setTimeout handlers 85 - jsPsych.pluginAPI.clearAllTimeouts(); 86 - 87 - // kill keyboard listeners 88 - if (typeof keyboardListener !== 'undefined') { 89 - jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener); 90 - } 91 - 92 - // gather the data to store for the trial 93 - var trial_data = { 94 - rt: response.rt, 95 - stimulus: trial.stimulus, 96 - key_press: response.key, 97 - }; 98 - 99 - // clear the display 100 - display_element.innerHTML = ''; 101 - 102 - // move on to the next trial 103 - jsPsych.finishTrial(trial_data); 104 - }; 105 - 106 - // function to handle responses by the subject 107 - var after_response = function(info) { 108 - // after a valid response, the stimulus will have the CSS class 'responded' 109 - // which can be used to provide visual feedback that a response was recorded 110 - display_element.querySelector('#jspsych-html-keyboard-response-stimulus').className += 111 - ' responded'; 112 - 113 - // only record the first response 114 - if (response.key == -1) { 115 - response = info; 116 - } 117 - 118 - if (trial.response_ends_trial) { 119 - end_trial(); 120 - } 121 - }; 122 - 123 - // start the response listener 124 - if (trial.choices != jsPsych.NO_KEYS) { 125 - var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({ 126 - callback_function: after_response, 127 - valid_responses: trial.choices, 128 - rt_method: 'performance', 129 - persist: false, 130 - allow_held_key: false, 131 - }); 132 - } 133 - 134 - // hide stimulus if stimulus_duration is set 135 - if (trial.stimulus_duration > 0) { 136 - jsPsych.pluginAPI.setTimeout(function() { 137 - display_element.querySelector('#jspsych-html-keyboard-response-stimulus').style.visibility = 138 - 'hidden'; 139 - }, trial.stimulus_duration); 140 - } 141 - 142 - // end trial if trial_duration is set 143 - if (trial.trial_duration > 0) { 144 - jsPsych.pluginAPI.setTimeout(function() { 145 - end_trial(); 146 - }, trial.trial_duration); 147 - } 148 - }; 149 - 150 - return plugin; 151 - })(); // IIFE closure 152 - 153 - export default plugin;
-158
app/utils/jspsych/plugins/callback-image-display.js
··· 1 - /** 2 - * callback-image-display 3 - * Teon L Brooks 4 - * modified from Josh de Leeuw 5 - * 6 - * plugin for displaying a stimulus and getting a keyboard response 7 - * 8 - * documentation: docs.jspsych.org 9 - * 10 - **/ 11 - import { jsPsych } from 'jspsych-react'; 12 - 13 - var plugin = (function() { 14 - var plugin = {}; 15 - 16 - jsPsych.pluginAPI.registerPreload('image-keyboard-response', 'stimulus', 'image'); 17 - 18 - plugin.info = { 19 - name: 'image-keyboard-response', 20 - description: '', 21 - parameters: { 22 - stimulus: { 23 - type: jsPsych.plugins.parameterType.IMAGE, 24 - pretty_name: 'stimulus', 25 - default: undefined, 26 - description: 'The image to be displayed', 27 - }, 28 - choices: { 29 - type: jsPsych.plugins.parameterType.KEYCODE, 30 - array: true, 31 - pretty_name: 'Choices', 32 - default: jsPsych.ALL_KEYS, 33 - description: 'The keys the subject is allowed to press to respond to the stimulus.', 34 - }, 35 - prompt: { 36 - type: jsPsych.plugins.parameterType.STRING, 37 - pretty_name: 'Prompt', 38 - default: '', 39 - description: 'Any content here will be displayed below the stimulus.', 40 - }, 41 - stimulus_duration: { 42 - type: jsPsych.plugins.parameterType.INT, 43 - pretty_name: 'Stimulus duration', 44 - default: -1, 45 - description: 'How long to hide the stimulus.', 46 - }, 47 - trial_duration: { 48 - type: jsPsych.plugins.parameterType.INT, 49 - pretty_name: 'Trial duration', 50 - default: -1, 51 - description: 'How long to show trial before it ends.', 52 - }, 53 - response_ends_trial: { 54 - type: jsPsych.plugins.parameterType.BOOL, 55 - pretty_name: 'Response ends trial', 56 - default: true, 57 - description: 'If true, trial will end when subject makes a response.', 58 - }, 59 - }, 60 - }; 61 - 62 - plugin.trial = function(display_element, trial) { 63 - document.getElementById('experiment').focus(); 64 - var new_html = 65 - '<img src="' + trial.stimulus + '" id="jspsych-image-keyboard-response-stimulus"></img>'; 66 - 67 - // add prompt 68 - new_html += trial.prompt; 69 - 70 - // add option for on_start parameter in the experiment 71 - if (typeof trial.on_start === 'function') { 72 - trial.on_start.call(); 73 - } else if (typeof trial.on_start !== 'undefined') { 74 - trial.data['on_start'] = trial.on_start; 75 - } 76 - 77 - // draw 78 - display_element.innerHTML = new_html; 79 - 80 - // store response 81 - var response = { 82 - rt: -1, 83 - key: -1, 84 - }; 85 - 86 - // function to end trial when it is time 87 - var end_trial = function() { 88 - // kill any remaining setTimeout handlers 89 - jsPsych.pluginAPI.clearAllTimeouts(); 90 - 91 - // kill keyboard listeners 92 - if (typeof keyboardListener !== 'undefined') { 93 - jsPsych.pluginAPI.cancelKeyboardResponse(keyboardListener); 94 - } 95 - 96 - // gather the data to store for the trial 97 - var trial_data = { 98 - rt: response.rt, 99 - stimulus: trial.stimulus, 100 - key_press: response.key, 101 - }; 102 - 103 - // clear the display 104 - display_element.innerHTML = ''; 105 - 106 - // move on to the next trial 107 - jsPsych.finishTrial(trial_data); 108 - }; 109 - 110 - // function to handle responses by the subject 111 - var after_response = function(info) { 112 - // after a valid response, the stimulus will have the CSS class 'responded' 113 - // which can be used to provide visual feedback that a response was recorded 114 - display_element.querySelector('#jspsych-image-keyboard-response-stimulus').className += 115 - ' responded'; 116 - 117 - // only record the first response 118 - if (response.key == -1) { 119 - response = info; 120 - } 121 - 122 - if (trial.response_ends_trial) { 123 - end_trial(); 124 - } 125 - }; 126 - 127 - // start the response listener 128 - if (trial.choices != jsPsych.NO_KEYS) { 129 - var keyboardListener = jsPsych.pluginAPI.getKeyboardResponse({ 130 - callback_function: after_response, 131 - valid_responses: trial.choices, 132 - rt_method: 'performance', 133 - persist: false, 134 - allow_held_key: false, 135 - }); 136 - } 137 - 138 - // hide stimulus if stimulus_duration is set 139 - if (trial.stimulus_duration > 0) { 140 - jsPsych.pluginAPI.setTimeout(function() { 141 - display_element.querySelector( 142 - '#jspsych-image-keyboard-response-stimulus' 143 - ).style.visibility = 'hidden'; 144 - }, trial.stimulus_duration); 145 - } 146 - 147 - // end trial if trial_duration is set 148 - if (trial.trial_duration > 0) { 149 - jsPsych.pluginAPI.setTimeout(function() { 150 - end_trial(); 151 - }, trial.trial_duration); 152 - } 153 - }; 154 - 155 - return plugin; 156 - })(); // IIFE closure 157 - 158 - export default plugin;
-154
app/utils/jspsych/plugins/jspsych-animation.js
··· 1 - /** 2 - * jsPsych plugin for showing animations and recording keyboard responses 3 - * Josh de Leeuw 4 - * 5 - * documentation: docs.jspsych.org 6 - */ 7 - import { jsPsych } from 'jspsych-react'; 8 - 9 - var plugin = (function() { 10 - var plugin = {}; 11 - 12 - jsPsych.pluginAPI.registerPreload('animation', 'stimuli', 'image'); 13 - 14 - plugin.info = { 15 - name: 'animation', 16 - description: '', 17 - parameters: { 18 - stimuli: { 19 - type: jsPsych.plugins.parameterType.STRING, 20 - pretty_name: 'Stimuli', 21 - default: undefined, 22 - array: true, 23 - description: 'The images to be displayed.', 24 - }, 25 - frame_time: { 26 - type: jsPsych.plugins.parameterType.INT, 27 - pretty_name: 'Frame time', 28 - default: 250, 29 - description: 'Duration to display each image.', 30 - }, 31 - frame_isi: { 32 - type: jsPsych.plugins.parameterType.INT, 33 - pretty_name: 'Frame gap', 34 - default: 0, 35 - description: 'Length of gap to be shown between each image.', 36 - }, 37 - sequence_reps: { 38 - type: jsPsych.plugins.parameterType.INT, 39 - pretty_name: 'Sequence repetitions', 40 - default: 1, 41 - description: 'Number of times to show entire sequence.', 42 - }, 43 - choices: { 44 - type: jsPsych.plugins.parameterType.KEYCODE, 45 - pretty_name: 'Choices', 46 - default: jsPsych.ALL_KEYS, 47 - array: true, 48 - description: 'Keys subject uses to respond to stimuli.', 49 - }, 50 - prompt: { 51 - type: jsPsych.plugins.parameterType.STRING, 52 - pretty_name: 'Prompt', 53 - default: null, 54 - description: 'Any content here will be displayed below stimulus.', 55 - }, 56 - }, 57 - }; 58 - 59 - plugin.trial = function(display_element, trial) { 60 - var interval_time = trial.frame_time + trial.frame_isi; 61 - var animate_frame = -1; 62 - var reps = 0; 63 - var startTime = new Date().getTime(); 64 - var animation_sequence = []; 65 - var responses = []; 66 - var current_stim = ''; 67 - 68 - var animate_interval = setInterval(function() { 69 - var showImage = true; 70 - display_element.innerHTML = ''; // clear everything 71 - animate_frame++; 72 - if (animate_frame == trial.stimuli.length) { 73 - animate_frame = 0; 74 - reps++; 75 - if (reps >= trial.sequence_reps) { 76 - endTrial(); 77 - clearInterval(animate_interval); 78 - showImage = false; 79 - } 80 - } 81 - if (showImage) { 82 - show_next_frame(); 83 - } 84 - }, interval_time); 85 - 86 - function show_next_frame() { 87 - // show image 88 - display_element.innerHTML = 89 - '<img src="' + trial.stimuli[animate_frame] + '" id="jspsych-animation-image"></img>'; 90 - 91 - current_stim = trial.stimuli[animate_frame]; 92 - 93 - // record when image was shown 94 - animation_sequence.push({ 95 - stimulus: trial.stimuli[animate_frame], 96 - time: new Date().getTime() - startTime, 97 - }); 98 - 99 - if (trial.prompt !== null) { 100 - display_element.innerHTML += trial.prompt; 101 - } 102 - 103 - if (trial.frame_isi > 0) { 104 - jsPsych.pluginAPI.setTimeout(function() { 105 - display_element.querySelector('#jspsych-animation-image').style.visibility = 'hidden'; 106 - current_stim = 'blank'; 107 - // record when blank image was shown 108 - animation_sequence.push({ 109 - stimulus: 'blank', 110 - time: new Date().getTime() - startTime, 111 - }); 112 - }, trial.frame_time); 113 - } 114 - } 115 - 116 - var after_response = function(info) { 117 - responses.push({ 118 - key_press: info.key, 119 - rt: info.rt, 120 - stimulus: current_stim, 121 - }); 122 - 123 - // after a valid response, the stimulus will have the CSS class 'responded' 124 - // which can be used to provide visual feedback that a response was recorded 125 - display_element.querySelector('#jspsych-animation-image').className += ' responded'; 126 - }; 127 - 128 - // hold the jspsych response listener object in memory 129 - // so that we can turn off the response collection when 130 - // the trial ends 131 - var response_listener = jsPsych.pluginAPI.getKeyboardResponse({ 132 - callback_function: after_response, 133 - valid_responses: trial.choices, 134 - rt_method: 'performance', 135 - persist: true, 136 - allow_held_key: false, 137 - }); 138 - 139 - function endTrial() { 140 - jsPsych.pluginAPI.cancelKeyboardResponse(response_listener); 141 - 142 - var trial_data = { 143 - animation_sequence: JSON.stringify(animation_sequence), 144 - responses: JSON.stringify(responses), 145 - }; 146 - 147 - jsPsych.finishTrial(trial_data); 148 - } 149 - }; 150 - 151 - return plugin; 152 - })(); 153 - 154 - export default plugin;
-71
app/utils/jspsych/timelines/custom.js
··· 1 - import * as path from 'path'; 2 - import { EVENTS } from '../../../constants/constants'; 3 - 4 - // Default directories containing stimuli 5 - const rootFolder = __dirname; // Note: there's a weird issue where the fs readdir function reads from BrainWaves dir 6 - 7 - const facesDir = path.join(rootFolder, 'assets', 'face_house', 'faces'); 8 - const housesDir = path.join(rootFolder, 'assets', 'face_house', 'houses'); 9 - const fixation = path.join(rootFolder, 'assets', 'common', 'fixationcross.png'); 10 - 11 - export const buildCustomLine = () => ({ 12 - overview: 'Here is the placeholder for the overview of the Visual Search task', 13 - background: 'Here is the background of the Visual Search task', 14 - background_title: `Title`, 15 - protocol: 'Here is the protocol of the Visual Search task', 16 - params: { 17 - trialDuration: 1000, 18 - nbTrials: 150, 19 - iti: 500, 20 - jitter: 200, 21 - sampleType: 'with-replacement', 22 - pluginName: 'callback-image-display', 23 - intro: 24 - 'You will see the visual search task. Press 1 when a face appears and 9 for a house. Press any key to continue', 25 - showProgressBar: false, 26 - stimulus1: { 27 - dir: facesDir, 28 - title: 'Face', 29 - type: EVENTS.STIMULUS_1, 30 - response: '1', 31 - }, 32 - stimulus2: { 33 - dir: housesDir, 34 - title: 'House', 35 - type: EVENTS.STIMULUS_2, 36 - response: '9', 37 - }, 38 - }, 39 - mainTimeline: ['intro', 'faceHouseTimeline', 'end'], // array of trial and timeline ids 40 - trials: { 41 - intro: { 42 - type: 'callback-html-display', 43 - id: 'intro', 44 - post_trial_gap: 1000, 45 - }, 46 - end: { 47 - id: 'end', 48 - type: 'callback-html-display', 49 - stimulus: 'Thanks for participating. Press any key to continue', 50 - response_ends_trial: true, 51 - post_trial_gap: 500, 52 - }, 53 - }, 54 - timelines: { 55 - faceHouseTimeline: { 56 - id: 'faceHouseTimeline', 57 - timeline: [ 58 - { 59 - id: 'interTrial', 60 - type: 'callback-image-display', 61 - stimulus: fixation, 62 - response_ends_trial: false, 63 - }, 64 - { 65 - id: 'trial', 66 - response_ends_trial: false, 67 - }, 68 - ], 69 - }, 70 - }, 71 - });
-87
app/utils/jspsych/timelines/multi.js
··· 1 - import * as path from 'path'; 2 - import { EVENTS } from '../../../constants/constants'; 3 - 4 - // Default directories containing stimuli 5 - const rootFolder = __dirname; // Note: there's a weird issue where the fs readdir function reads from BrainWaves dir 6 - 7 - const facesDir = path.join(rootFolder, 'assets', 'face_house', 'faces'); 8 - const housesDir = path.join(rootFolder, 'assets', 'face_house', 'houses'); 9 - const fixation = path.join(rootFolder, 'assets', 'common', 'fixationcross.png'); 10 - 11 - export const buildMultiTimeline = () => ({ 12 - overview_title: `Multi-Tasking Experiment`, 13 - overview: `Imagine doing your homework while watching television. 14 - Multitasking can be defined as our (in)ability to complete multiple tasks at the same time.`, 15 - background_first_column: `We are constantly facing different tasks at the same time: 16 - doing your homework while watching TV, writing emails and also responding to your friend’s text messages, etc. 17 - Some people pride themselves with being “good multitaskers.” But is this scientifically plausible?`, 18 - background_first_column_question: `Are some people better than others at performing multiple tasks at the same time?`, 19 - background_second_column: `Some research suggests that good multitaskers are actually not performing different tasks simultaneously, 20 - but are instead rapidly switching back ‘n forth between different tasks (click here to read more about this research). 21 - Other research suggests that our brains are able to distribute different tasks across hemispheres 22 - (you can read more about it here).`, 23 - background_second_column_question: `Do you think some people could be better brain ‘distributors’ than others?`, 24 - protocol_title: `What participants are shown`, 25 - protocol: `Participants are shown either a square or diamonds with dots inside. 26 - The location of the object on the screen indicates which rule the participant needs to follow`, 27 - protocol_condition_first_img: `multiConditionShape`, 28 - protocol_condition_first_title: `Rule 1`, 29 - protocol_condition_first: `If the object is shown on top, they need to respond to the shape (pressing ‘n’ for square and ‘b’ for diamond).`, 30 - protocol_condition_second_img: `multiConditionDots`, 31 - protocol_condition_second_title: `Rule 2`, 32 - protocol_condition_second: `If the object is shown on the bottom, they need to respond to the number of dots inside (pressing ‘n’ for 3 dots and ‘b’ for 2 dots). `, 33 - params: { 34 - trialDuration: 1000, 35 - nbTrials: 150, 36 - iti: 500, 37 - jitter: 200, 38 - sampleType: 'with-replacement', 39 - pluginName: 'callback-image-display', 40 - intro: 'You will see the multi-tasking test. Press any key to continue', 41 - showProgressBar: false, 42 - stimulus1: { 43 - dir: facesDir, 44 - title: 'No switching', 45 - type: EVENTS.STIMULUS_1, 46 - response: '1', 47 - }, 48 - stimulus2: { 49 - dir: housesDir, 50 - title: 'Switching', 51 - type: EVENTS.STIMULUS_2, 52 - response: '9', 53 - }, 54 - }, 55 - mainTimeline: ['intro', 'faceHouseTimeline', 'end'], // array of trial and timeline ids 56 - trials: { 57 - intro: { 58 - type: 'callback-html-display', 59 - id: 'intro', 60 - post_trial_gap: 1000, 61 - }, 62 - end: { 63 - id: 'end', 64 - type: 'callback-html-display', 65 - stimulus: 'Thanks for participating. Press any key to continue', 66 - response_ends_trial: true, 67 - post_trial_gap: 500, 68 - }, 69 - }, 70 - timelines: { 71 - faceHouseTimeline: { 72 - id: 'faceHouseTimeline', 73 - timeline: [ 74 - { 75 - id: 'interTrial', 76 - type: 'callback-image-display', 77 - stimulus: fixation, 78 - response_ends_trial: false, 79 - }, 80 - { 81 - id: 'trial', 82 - response_ends_trial: false, 83 - }, 84 - ], 85 - }, 86 - }, 87 - });
-89
app/utils/jspsych/timelines/n170.js
··· 1 - import * as path from 'path'; 2 - import { EVENTS } from '../../../constants/constants'; 3 - 4 - // Default directories containing stimuli 5 - const rootFolder = __dirname; // Note: there's a weird issue where the fs readdir function reads from BrainWaves dir 6 - 7 - const facesDir = path.join(rootFolder, 'assets', 'face_house', 'faces'); 8 - const housesDir = path.join(rootFolder, 'assets', 'face_house', 'houses'); 9 - const fixation = path.join(rootFolder, 'assets', 'common', 'fixationcross.png'); 10 - 11 - export const buildN170Timeline = () => ({ 12 - overview_title: `Faces Houses Experiment`, 13 - overview: `When you scroll through your social media feed, you may find that you are more likely to pause when a picture 14 - contains a face, than, for example, a tree. 15 - In this experiment, you will explore whether our brains process faces differently than other objects (in this case, houses).`, 16 - background_first_column: `Did you know that we spend more time looking at faces than any other type of stimuli? 17 - Faces contain a lot of information that is relevant to our day-to-day lives. 18 - For example, by looking at someone’s face we can assess their emotional state. 19 - This has led researchers speculate that faces may be processed differently than other stimuli.`, 20 - background_first_column_question: `In fact, there is a special area in your brain, the Fusiform Face Area, that has been shown to be selective for faces. 21 - People who have damage in this area may have a hard time recognizing faces, a condition called face blindness, or prosopagnosia.`, 22 - background_second_column: `In this video the famous neurologist Oliver Sacks explains what it is like to have face blindness to the extent 23 - that he sometimes didn’t even recognize his own face (!)`, 24 - background_second_column_question: `Fun fact: Brad Pitt claims he has face blindness, but he has not been tested. 25 - Do you know anyone who has face blindness?`, 26 - protocol_title: `What participants are shown`, 27 - protocol: `In the Faces/Houses experiment, you will see pictures of different faces and houses.`, 28 - protocol_condition_first_img: `conditionFace`, 29 - protocol_condition_first_title: `Faces`, 30 - protocol_condition_first: `When you see a face, press the key “1”.`, 31 - protocol_condition_second_img: `conditionHouse`, 32 - protocol_condition_second_title: `Houses`, 33 - protocol_condition_second: `If you see a house, press “9”.`, 34 - params: { 35 - trialDuration: 1000, 36 - nbTrials: 150, 37 - iti: 500, 38 - jitter: 200, 39 - sampleType: 'with-replacement', 40 - pluginName: 'callback-image-display', 41 - intro: 42 - 'You will view a series of faces and houses. Press 1 when a face appears and 9 for a house. Press any key to continue', 43 - showProgressBar: false, 44 - stimulus1: { 45 - dir: facesDir, 46 - title: 'Face', 47 - type: EVENTS.STIMULUS_1, 48 - response: '1', 49 - }, 50 - stimulus2: { 51 - dir: housesDir, 52 - title: 'House', 53 - type: EVENTS.STIMULUS_2, 54 - response: '9', 55 - }, 56 - }, 57 - mainTimeline: ['intro', 'faceHouseTimeline', 'end'], // array of trial and timeline ids 58 - trials: { 59 - intro: { 60 - type: 'callback-html-display', 61 - id: 'intro', 62 - post_trial_gap: 1000, 63 - }, 64 - end: { 65 - id: 'end', 66 - type: 'callback-html-display', 67 - stimulus: 'Thanks for participating. Press any key to continue', 68 - response_ends_trial: true, 69 - post_trial_gap: 500, 70 - }, 71 - }, 72 - timelines: { 73 - faceHouseTimeline: { 74 - id: 'faceHouseTimeline', 75 - timeline: [ 76 - { 77 - id: 'interTrial', 78 - type: 'callback-image-display', 79 - stimulus: fixation, 80 - response_ends_trial: false, 81 - }, 82 - { 83 - id: 'trial', 84 - response_ends_trial: false, 85 - }, 86 - ], 87 - }, 88 - }, 89 - });
-92
app/utils/jspsych/timelines/oddball.js
··· 1 - import { jsPsych } from 'jspsych-react'; 2 - import * as path from 'path'; 3 - import { readdirSync } from 'fs'; 4 - 5 - // Default experiment parameters 6 - const params = { 7 - trial_duration: 300, 8 - stim_duration: 300, 9 - iti: 300, 10 - jitter: 200, 11 - n_trials: 170, // Around two minutes at a rate of ~700 ms per trial 12 - prob: 0.15, 13 - plugin_name: 'callback-image-display', 14 - }; 15 - 16 - // Default directories containing stimuli 17 - // Note: there's a weird issue where the fs readdir function reads from BrainWaves dir 18 - // while the timeline reads from Brainwaves/app. Currently removing 'app/' from path in timeline 19 - const rootFolder = __dirname; 20 - const targetsDir = path.join(rootFolder, './app/assets/cat_dog/cats/'); 21 - const nontargetsDir = path.join(rootFolder, './app/assets/cat_dog/dogs/'); 22 - 23 - // Oddball sampling function 24 - // Assumes first half of the trials are oddball stimuli 25 - // TODO: Make this autogenerate from reading dir 26 - const oddballSamplingFn = (trials) => { 27 - const trialOrder = new Array(params.n_trials).fill(0).map(() => { 28 - if (Math.random() > params.prob) { 29 - return Math.floor(Math.random() * (trials.length - trials.length / 2) + trials.length / 2); 30 - } 31 - return Math.floor(Math.random() * (trials.length / 2)); 32 - }); 33 - return trialOrder; 34 - }; 35 - 36 - export const buildOddballTimeline = (callback) => ({ 37 - mainTimeline: ['welcome', 'oddballTimeline', 'end'], // array of trial and timeline ids 38 - trials: { 39 - welcome: { 40 - type: 'callback-html-display', 41 - id: 'welcome', 42 - stimulus: 'Welcome to the experiment. Press any key to begin.', 43 - post_trial_gap: 1000, 44 - on_load: () => callback('start'), 45 - }, 46 - end: { 47 - id: 'end', 48 - type: 'callback-html-display', 49 - stimulus: 'Thanks for participating', 50 - post_trial_gap: 500, 51 - on_load: callback('stop'), 52 - }, 53 - }, 54 - timelines: { 55 - oddballTimeline: { 56 - id: 'oddballTimeline', 57 - timeline: [ 58 - { 59 - id: 'interTrial', 60 - type: 'callback-image-display', 61 - stimulus: './assets/cat_dog/fixation.jpg', 62 - trial_duration: () => params.iti + Math.random() * params.jitter, 63 - post_trial_gap: 0, 64 - }, 65 - { 66 - id: 'trial', 67 - stimulus: jsPsych.timelineVariable('stimulusVar'), 68 - on_load: jsPsych.timelineVariable('callbackVar'), 69 - type: params.plugin_name, 70 - choices: ['f', 'j'], 71 - trial_duration: params.trial_duration, 72 - post_trial_gap: 0, 73 - }, 74 - ], 75 - sample: { 76 - type: 'custom', 77 - fn: oddballSamplingFn, 78 - }, 79 - timeline_variables: readdirSync(targetsDir) 80 - .map((filename) => ({ 81 - stimulusVar: targetsDir + filename, 82 - callbackVar: () => callback('target'), 83 - })) 84 - .concat( 85 - readdirSync(nontargetsDir).map((filename) => ({ 86 - stimulusVar: nontargetsDir + filename, 87 - callbackVar: () => callback('nontarget'), 88 - })) 89 - ), 90 - }, 91 - }, 92 - });
-91
app/utils/jspsych/timelines/search.js
··· 1 - import * as path from 'path'; 2 - import { EVENTS } from '../../../constants/constants'; 3 - 4 - // Default directories containing stimuli 5 - const rootFolder = __dirname; // Note: there's a weird issue where the fs readdir function reads from BrainWaves dir 6 - 7 - const facesDir = path.join(rootFolder, 'assets', 'face_house', 'faces'); 8 - const housesDir = path.join(rootFolder, 'assets', 'face_house', 'houses'); 9 - const fixation = path.join(rootFolder, 'assets', 'common', 'fixationcross.png'); 10 - 11 - export const buildSearchTimeline = () => ({ 12 - overview_title: `Visual Search Experiment`, 13 - overview: `Imagine yourself looking for your keys in a messy room. 14 - Visual search corresponds to looking for a specific object (e.g. keys) surrounded by other objects (e.g. clothing and books). 15 - In the Visual Search Task, you will explore how irrelevant objects affect how well you can find the object you are looking for, 16 - and what this may tell us about how your brain can find and focus on relevant information from all the task-irrelevant 17 - information around us.`, 18 - background_first_column: `Say you’re meeting a friend in a public space: do you think it easier to find each other on a street corner 19 - than in the middle of Times Square around rush hour? What about a parking lot filled with cars? 20 - Scientists have long wondered how our brains allow us to navigate a world full of irrelevant visual information with such relative ease. 21 - One way to investigate this is to ask people to complete a visual search task.`, 22 - background_first_column_question: ``, 23 - background_second_column: `The hypothesis would be that if you see objects that are very similar to what you are looking for 24 - (like other people who might look like your friend, as opposed to parked cars), 25 - these might distract you, making it harder to complete your search. 26 - Brain scientists aren’t the only ones who are interested in exploring the most optimal way in which visual searches occur.`, 27 - background_second_column_question: `Can you think of who else might want to know this?`, 28 - protocol_title: `What participants are shown`, 29 - protocol: `In the Visual Search Task, your goal is to find the right-side up orange T while ignoring upside-down orange T’s or T’s in other colors.`, 30 - protocol_condition_first_img: `conditionOrangeT`, 31 - protocol_condition_first_title: `Orange T`, 32 - protocol_condition_first: `If you find the orange T, you should press the ‘b’ key. `, 33 - protocol_condition_second_img: `conditionNoOrangeT`, 34 - protocol_condition_second_title: `No orange T`, 35 - protocol_condition_second: `If the orange T is not on the screen, press the ‘n’ key instead.`, 36 - params: { 37 - trialDuration: 1000, 38 - nbTrials: 150, 39 - iti: 500, 40 - jitter: 200, 41 - sampleType: 'with-replacement', 42 - pluginName: 'callback-image-display', 43 - intro: 44 - 'You will see the visual search task. Press 1 when a face appears and 9 for a house. Press any key to continue', 45 - showProgressBar: false, 46 - stimulus1: { 47 - dir: facesDir, 48 - title: 'No target', 49 - type: EVENTS.STIMULUS_1, 50 - response: '1', 51 - }, 52 - stimulus2: { 53 - dir: housesDir, 54 - title: 'Target', 55 - type: EVENTS.STIMULUS_2, 56 - response: '9', 57 - }, 58 - }, 59 - mainTimeline: ['intro', 'faceHouseTimeline', 'end'], // array of trial and timeline ids 60 - trials: { 61 - intro: { 62 - type: 'callback-html-display', 63 - id: 'intro', 64 - post_trial_gap: 1000, 65 - }, 66 - end: { 67 - id: 'end', 68 - type: 'callback-html-display', 69 - stimulus: 'Thanks for participating. Press any key to continue', 70 - response_ends_trial: true, 71 - post_trial_gap: 500, 72 - }, 73 - }, 74 - timelines: { 75 - faceHouseTimeline: { 76 - id: 'faceHouseTimeline', 77 - timeline: [ 78 - { 79 - id: 'interTrial', 80 - type: 'callback-image-display', 81 - stimulus: fixation, 82 - response_ends_trial: false, 83 - }, 84 - { 85 - id: 'trial', 86 - response_ends_trial: false, 87 - }, 88 - ], 89 - }, 90 - }, 91 - });
-74
app/utils/jspsych/timelines/ssvep.js
··· 1 - import { jsPsych } from 'jspsych-react'; 2 - import * as path from 'path'; 3 - 4 - // Default experiment parameters 5 - const params = { 6 - stim_duration: 3000, 7 - iti: 500, 8 - jitter: 200, 9 - n_trials: 34, // Around two minutes at a rate of ~3600 ms per trial 10 - plugin_name: 'animation', 11 - }; 12 - 13 - const rootFolder = __dirname; 14 - 15 - export const buildSSVEPTimeline = (callback) => ({ 16 - mainTimeline: ['welcome', 'ssvepTimeline', 'end'], // array of trial and timeline ids 17 - trials: { 18 - welcome: { 19 - type: 'callback-html-display', 20 - id: 'welcome', 21 - stimulus: 'Welcome to the experiment. Press any key to begin.', 22 - post_trial_gap: 1000, 23 - on_load: () => callback('start'), 24 - }, 25 - end: { 26 - id: 'end', 27 - type: 'callback-html-display', 28 - stimulus: 'Thanks for participating', 29 - post_trial_gap: 500, 30 - on_load: callback('stop'), 31 - }, 32 - }, 33 - timelines: { 34 - ssvepTimeline: { 35 - id: 'ssvepTimeline', 36 - timeline: [ 37 - { 38 - id: 'interTrial', 39 - type: 'callback-image-display', 40 - stimulus: path.join(rootFolder, 'assets/face_house/fixation.jpg'), 41 - trial_duration: () => params.iti + Math.random() * params.jitter, 42 - }, 43 - { 44 - id: 'trial', 45 - stimuli: [ 46 - path.join(rootFolder, '/assets/ssvep/Checkerboard_pattern.svg'), 47 - path.join(rootFolder, '/assets/ssvep/Checkerboard_pattern_neg.svg'), 48 - ], 49 - on_load: jsPsych.timelineVariable('callbackVar'), 50 - type: params.plugin_name, 51 - frame_time: jsPsych.timelineVariable('stim_periodVar'), 52 - sequence_reps: jsPsych.timelineVariable('repsVar'), 53 - }, 54 - ], 55 - sample: { 56 - type: 'with-replacement', 57 - size: params.n_trials, 58 - }, 59 - timeline_variables: [ 60 - // reps uses a weird equation I had to derive to get stimulus duration to match up to our parameter 61 - { 62 - stim_periodVar: 1000 / 30, 63 - repsVar: Math.floor(params.stim_duration / (1000 / 30) - 1) / 2, 64 - callbackVar: () => callback('30'), 65 - }, 66 - { 67 - stim_periodVar: 1000 / 20, 68 - repsVar: Math.floor(params.stim_duration / (1000 / 20) - 1) / 2, 69 - callbackVar: () => callback('20'), 70 - }, 71 - ], 72 - }, 73 - }, 74 - });
-91
app/utils/jspsych/timelines/stroop.js
··· 1 - import * as path from 'path'; 2 - import { EVENTS } from '../../../constants/constants'; 3 - 4 - // Default directories containing stimuli 5 - const rootFolder = __dirname; // Note: there's a weird issue where the fs readdir function reads from BrainWaves dir 6 - 7 - const facesDir = path.join(rootFolder, 'assets', 'face_house', 'faces'); 8 - const housesDir = path.join(rootFolder, 'assets', 'face_house', 'houses'); 9 - const fixation = path.join(rootFolder, 'assets', 'common', 'fixationcross.png'); 10 - 11 - export const buildStroopTimeline = () => ({ 12 - overview_title: `Stroop Experiment`, 13 - overview: `The stroop effect occurs when different properties of something you see or hear contradict one another. 14 - The most common example is a mismatch between the meaning and color of a word (e.g., the word BLUE written in red ink). 15 - In the Stroop task, you will explore how these types of mismatches affect your behavior and what that may tell us about 16 - how your brain processes information.`, 17 - background_first_column: `You may have played a game with your friends or siblings with the following seemingly simple rule: 18 - “yes” means “no” and “no” means “yes”. If so, you are all too familiar with Stroop-like effect: 19 - the information you receive points to different concepts and you have to “ignore” one source of information.`, 20 - background_first_column_question: `Fun fact: in some Balkan countries, nodding means “no” and shaking your head means “yes”.`, 21 - background_second_column: `Researchers have used different kinds of Stroop tasks to ask how our brains deal with contradictory information. 22 - This may be more difficult under some conditions (for example, when we don’t get enough sleep) and for some people 23 - (for example, children with Attention-deficit/hyperactivity disorder (ADHD)).`, 24 - background_second_column_question: `You can read more about the Stroop task here. `, 25 - protocol_title: `What participants are shown`, 26 - protocol: `In the Stroop task, you will see different words written in different colors 27 - (e.g., the word “GREEN” may be written in a green-colored font, but it may also be written in a red font). 28 - You will respond only to the color of the font, ignoring the meaning of the word. 29 - If the font is red, press the key ‘r’; if yellow, press ‘y’; if blue, press ‘b’; and if green, press ‘g’.`, 30 - protocol_condition_first_img: `conditionCongruent`, 31 - protocol_condition_first_title: `"Green" written in green`, 32 - protocol_condition_first: `The color is green, so the correct response is ‘g’.`, 33 - protocol_condition_second_img: `conditionIncongruent`, 34 - protocol_condition_second_title: `"Green" written in red`, 35 - protocol_condition_second: `The color is red, so the correct response is ‘r’.`, 36 - params: { 37 - trialDuration: 1000, 38 - nbTrials: 150, 39 - iti: 500, 40 - jitter: 200, 41 - sampleType: 'with-replacement', 42 - pluginName: 'callback-image-display', 43 - intro: 44 - 'You will see the stroop task. Press 1 when a face appears and 9 for a house. Press any key to continue', 45 - showProgressBar: false, 46 - stimulus1: { 47 - dir: facesDir, 48 - title: 'Incongruent', 49 - type: EVENTS.STIMULUS_1, 50 - response: '1', 51 - }, 52 - stimulus2: { 53 - dir: housesDir, 54 - title: 'Congruent', 55 - type: EVENTS.STIMULUS_2, 56 - response: '9', 57 - }, 58 - }, 59 - mainTimeline: ['intro', 'faceHouseTimeline', 'end'], // array of trial and timeline ids 60 - trials: { 61 - intro: { 62 - type: 'callback-html-display', 63 - id: 'intro', 64 - post_trial_gap: 1000, 65 - }, 66 - end: { 67 - id: 'end', 68 - type: 'callback-html-display', 69 - stimulus: 'Thanks for participating. Press any key to continue', 70 - response_ends_trial: true, 71 - post_trial_gap: 500, 72 - }, 73 - }, 74 - timelines: { 75 - faceHouseTimeline: { 76 - id: 'faceHouseTimeline', 77 - timeline: [ 78 - { 79 - id: 'interTrial', 80 - type: 'callback-image-display', 81 - stimulus: fixation, 82 - response_ends_trial: false, 83 - }, 84 - { 85 - id: 'trial', 86 - response_ends_trial: false, 87 - }, 88 - ], 89 - }, 90 - }, 91 - });
-1
app/utils/labjs/protocols/custom.js
··· 46 46 selfPaced: true, 47 47 jitter: 200, 48 48 sampleType: 'with-replacement', 49 - pluginName: 'callback-image-display', 50 49 intro: '', 51 50 taskHelp: '', 52 51 showProgressBar: false,
-1
app/utils/labjs/protocols/faceshouses.js
··· 68 68 selfPaced: true, 69 69 jitter: 200, 70 70 sampleType: 'with-replacement', 71 - pluginName: 'callback-image-display', 72 71 intro: `You will view a series of faces and houses. Press 1 when a face appears and 9 for a house. Press the the space bar on your keyboard to start doing the practice trials. If you want to skip the practice trials and go directly to the task, press the "q" button on your keyboard.`, 73 72 taskHelp: `Press 1 for a face and 9 for a house`, 74 73 showProgressBar: false,
-1
app/utils/labjs/protocols/multi.js
··· 49 49 iti: 1000, 50 50 jitter: 200, 51 51 sampleType: 'with-replacement', 52 - pluginName: 'callback-image-display', 53 52 intro: `In this task you will learn about multitasking difficulties using a task mixing and switching paradigm. You will go through several instruction and training blocks and then several blocks of real data collection will follow. Press the space bar to continue with the instructions.`, 54 53 showProgressBar: false, 55 54 stimulus1: {
-1
app/utils/labjs/protocols/search.js
··· 42 42 iti: 500, 43 43 jitter: 200, 44 44 sampleType: 'with-replacement', 45 - pluginName: 'callback-image-display', 46 45 intro: `You know how difficult it is to find your keys in a messy room. We want to know how good you are in quickly finding your keys. Instead of keys, we just want to know how quickly you can find an orange T amongst blue Ts and upside-down orange Ts. Sounds easy! But it is not at all that easy!`, 47 46 showProgressBar: false, 48 47 stimulus1: {
-1
app/utils/labjs/protocols/stroop.js
··· 48 48 iti: 500, 49 49 jitter: 200, 50 50 sampleType: 'with-replacement', 51 - pluginName: 'callback-image-display', 52 51 intro: `In this experiment, your task will be to identify the color of the word shown on the screen. The word itself is immaterial - you can safely ignore it.`, 53 52 showProgressBar: false, 54 53 stimulus1: {
-1
package.json
··· 234 234 "font-awesome": "^4.7.0", 235 235 "hazardous": "^0.3.0", 236 236 "history": "^4.7.2", 237 - "jspsych-react": "^0.3.0", 238 237 "kernelspecs": "^2.0.0", 239 238 "lab.js": "^20.0.1", 240 239 "lodash": "^4.17.15",