-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.js
293 lines (253 loc) · 8.1 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
let jsPsych = initJsPsych({
override_safe_mode : true,
extensions: [
{type: jsPsychExtensionWebgazer}
],
exclusions: {
min_width: MIN_WIDTH,
min_height: MIN_HEIGHT
},
on_finish: function() {
uil.saveData(ACCESS_KEY);
}
});
let preload = {
type: jsPsychPreload,
images: [],
}
let browser_data = {
type: jsPsychCallFunction,
func: () => {uil.browser.getResolutionInfo()}
};
let enter_fullscreen = {
type: jsPsychFullscreen,
fullscreen_mode: true
}
let sound_test_instructions = {
type: jsPsychHtmlButtonResponse,
stimulus: `
<p>We will now play a test sound</p>
`,
choices: ['Got it'],
}
let camera_instructions = {
type: jsPsychHtmlButtonResponse,
stimulus: `
<p>In order to participate you must allow the experiment to use your camera.</p>
<p>You will be prompted to do this on the next screen.</p>
<p>If you do not wish to allow use of your camera, you cannot participate in this experiment.<p>
<p>It may take up to 30 seconds for the camera to initialize after you give permission.</p>
`,
choices: ['Got it'],
}
let init_camera = {
type: jsPsychWebgazerInitCamera
}
let calibration_instructions = {
type: jsPsychHtmlButtonResponse,
stimulus: `
<p>Now you'll calibrate the eye tracking, so that the software can use the image of your eyes to predict where you are looking.</p>
<p>You'll see a series of dots appear on the screen</p>
<p>Look at each dot as it appears on the screen, and keep looking until it disappears.</p>
`,
choices: ['Got it'],
}
let calibration = {
type: jsPsychWebgazerCalibrate,
calibration_points: CALIBRATION_POINTS,
repetitions_per_point: 2,
randomize_calibration_order: true,
calibration_mode: CALIBRATION_CLICK ? 'click':'view',
}
let validation_instructions = {
type: jsPsychHtmlButtonResponse,
stimulus: `
<p>Now we'll measure the accuracy of the calibration.</p>
<p>Look at each dot as it appears on the screen, and keep looking until it disappears.</p>
`,
choices: ['Got it'],
post_trial_gap: 1000
}
let validation = {
type: jsPsychWebgazerValidate,
validation_points: CALIBRATION_POINTS,
roi_radius: 200,
time_to_saccade: 1000,
validation_duration: 2000,
show_validation_data: true,
data: {
task: 'validate'
}
}
let recalibrate_instructions = {
type: jsPsychHtmlButtonResponse,
stimulus: `
<p>The accuracy of the calibration is a little lower than we'd like.</p>
<p>Let's try calibrating one more time.</p>
<p>Look at each dot as it appears on the screen, and keep looking until it disappears.</p>
`,
choices: ['OK'],
}
let hide_dot = {
type: jsPsychCallFunction,
func: function() {
jsPsych.extensions["webgazer"].hidePredictions();
}
}
let recalibrate = {
timeline: [hide_dot, recalibrate_instructions, calibration, validation_instructions, validation, hide_dot],
conditional_function: function(){
let validation_data = jsPsych.data.get().filter({task: 'validate'}).values()[0];
return validation_data.percent_in_roi.some(function(x){
let minimum_percent_acceptable = 50;
return x < minimum_percent_acceptable;
});
},
data: {
phase: 'recalibration'
}
}
let calibration_done = {
type: jsPsychHtmlButtonResponse,
stimulus: `
<p>Great, we're done with calibration!</p>
`,
choices: ['OK']
}
let begin_practice = {
type: jsPsychHtmlKeyboardResponse,
stimulus: `<p>Instructions for the practice phase go here</p>
<p>Press any key to start.</p>`
}
let begin_test = {
type: jsPsychHtmlKeyboardResponse,
stimulus: `<p>Instructions for the test phase go here</p>
<p>Press any key to start.</p>`
}
let trial = {
type: jsPsychAudioKeyboardResponse,
stimulus: jsPsych.timelineVariable('sound'),
prompt: () => '<img id="image-stimulus" src="'+jsPsych.timelineVariable('image')+'"/>',
choices: "NO_KEYS",
trial_duration: 5000,
extensions: [
{
type: jsPsychExtensionWebgazer,
params: {targets: ['#image-stimulus']}
}
],
on_finish: function(data) {
data.id = jsPsych.timelineVariable('id');
data.item_type = jsPsych.timelineVariable('item_type');
data.image = jsPsych.timelineVariable('image');
data.sound = jsPsych.timelineVariable('sound');
}
}
let start_screen = {
type: jsPsychHtmlButtonResponse,
stimulus: function() {
return "<div class='instruction' >" +
"<p>Initial instructions go here</p></div>";
},
choices: [OK_BUTTON_TEXT],
response_ends_trial: true
};
let end_experiment = {
type : jsPsychHtmlKeyboardResponse,
stimulus : '<p>This is the end of the experiment</p>',
choices : [],
on_load: function() {
if (consent_given) {
uil.saveData();
}
else {
document.body.innerHTML = FINISHED_NO_CONSENT;
}
}
};
function getTimeline(stimuli) {
let timeline = [];
timeline.push(preload);
timeline.push(start_screen);
timeline.push(consent_procedure);
timeline.push(survey_procedure);
timeline.push(sound_test_instructions);
timeline.push(test_audio_looped);
timeline.push(browser_data);
timeline.push(camera_instructions);
timeline.push(init_camera);
timeline.push(enter_fullscreen);
timeline.push(calibration_instructions);
timeline.push(calibration);
timeline.push(validation_instructions);
timeline.push(validation);
timeline.push(hide_dot);
timeline.push(recalibrate);
timeline.push(calibration_done);
timeline.push(begin_practice);
let practice = {
timeline: [
trial
],
timeline_variables: getPracticeItems().table,
randomize_order: false,
};
timeline.push(practice);
timeline.push(begin_test);
let test = {
timeline: [
trial,
],
timeline_variables: stimuli.table
}
timeline.push(test);
timeline.push(end_experiment);
return timeline;
}
function main() {
// Make sure you have updated your key in globals.js
uil.setAccessKey(ACCESS_KEY);
uil.stopIfExperimentClosed();
// Option 1: client side randomization:
let stimuli = pickRandomList();
kickOffExperiment(stimuli, getTimeline(stimuli));
// Option 2: server side balancing:
// Make sure you have matched your groups on the dataserver with the
// lists in stimuli.js..
// This experiment uses groups/lists list1, and list2 by default (see
// stimuli.js).
// Hence, unless you change lists here, you should created matching
// groups there.
// uil.session.start(ACCESS_KEY, (group_name) => {
// let stimuli = findList(group_name);
// kickOffExperiment(stimuli, getTimeline(stimuli));
// });
}
// this function will eventually run the jsPsych timeline
function kickOffExperiment(stimuli, timeline) {
let subject_id = uil.session.isActive() ?
uil.session.subjectId() : jsPsych.randomization.randomID(8);
let test_items = stimuli.table;
let list_name = stimuli.list_name;
if (PSEUDO_RANDOMIZE) {
let shuffled = uil.randomization.randomizeStimuli(
test_items,
max_same_type=MAX_SUCCEEDING_ITEMS_OF_TYPE
);
if (shuffled !== null)
test_items = shuffled;
else
console.error('Unable to shuffle stimuli according constraints.')
}
// data one would like to add to __all__ trials, according to:
// https://www.jspsych.org/overview/data/
jsPsych.data.addProperties (
{
subject : subject_id,
list : list_name,
}
);
// Start jsPsych when running on a Desktop or Laptop style pc.
uil.browser.rejectMobileOrTablet();
jsPsych.run(timeline);
}