Hello! I want to have participants record their video consent, as I am collecting adolescent data and want to verify that the adolescent is in fact an adolescent. However, the videos do not seem to be recording. When I download the data from Pavlovia, the download folder is empty. I can see the camera is on, but I am not sure why the videos are not being saved to Pavlovia. The template code is below! I’d also ultimately like to redirect participants to a Qualtrics survey. Thanks!
/***************
- Webcam Test *
***************/
import { core, data, sound, util, visual, hardware } from ‘./lib/psychojs-2022.2.5.js’;
const { PsychoJS } = core;
const { TrialHandler, MultiStairHandler } = data;
const { Scheduler } = util;
//some handy aliases as in the psychopy scripts;
const { abs, sin, cos, PI: pi, sqrt } = Math;
const { round } = util;
// store info about the experiment session:
let expName = ‘webcam’; // from the Builder filename that created this script
let expInfo = {
‘participant’: ${util.pad(Number.parseFloat(util.randint(0, 999999)).toFixed(0), 6)}
,
‘session’: ‘001’,
};
// Start code blocks for ‘Before Experiment’
// init psychoJS:
const psychoJS = new PsychoJS({
debug: true
});
// open window:
psychoJS.openWindow({
fullscr: true,
color: new util.Color([0,0,0]),
units: ‘height’,
waitBlanking: true
});
// schedule the experiment:
psychoJS.schedule(psychoJS.gui.DlgFromDict({
dictionary: expInfo,
title: expName
}));
const flowScheduler = new Scheduler(psychoJS);
const dialogCancelScheduler = new Scheduler(psychoJS);
psychoJS.scheduleCondition(function() { return (psychoJS.gui.dialogComponent.button === ‘OK’); }, flowScheduler, dialogCancelScheduler);
// flowScheduler gets run if the participants presses OK
flowScheduler.add(updateInfo); // add timeStamp
flowScheduler.add(experimentInit);
flowScheduler.add(instructionRoutineBegin());
flowScheduler.add(instructionRoutineEachFrame());
flowScheduler.add(instructionRoutineEnd());
flowScheduler.add(trialRoutineBegin());
flowScheduler.add(trialRoutineEachFrame());
flowScheduler.add(trialRoutineEnd());
flowScheduler.add(thnksRoutineBegin());
flowScheduler.add(thnksRoutineEachFrame());
flowScheduler.add(thnksRoutineEnd());
flowScheduler.add(quitPsychoJS, ‘’, true);
// quit if user presses Cancel in dialog box:
dialogCancelScheduler.add(quitPsychoJS, ‘’, false);
psychoJS.start({
expName: expName,
expInfo: expInfo,
resources: [
]
});
psychoJS.experimentLogger.setLevel(core.Logger.ServerLevel.EXP);
var currentLoop;
var frameDur;
async function updateInfo() {
currentLoop = psychoJS.experiment; // right now there are no loops
expInfo[‘date’] = util.MonotonicClock.getDateStr(); // add a simple timestamp
expInfo[‘expName’] = expName;
expInfo[‘psychopyVersion’] = ‘2022.2.5’;
expInfo[‘OS’] = window.navigator.platform;
// store frame rate of monitor if we can measure it successfully
expInfo[‘frameRate’] = psychoJS.window.getActualFrameRate();
if (typeof expInfo[‘frameRate’] !== ‘undefined’)
frameDur = 1.0 / Math.round(expInfo[‘frameRate’]);
else
frameDur = 1.0 / 60.0; // couldn’t get a reliable measure so guess
// add info from the URL:
util.addInfoFromUrl(expInfo);
psychoJS.experiment.dataFileName = ((“.” + “/”) + data/${expInfo["participant"]}_${expName}_${expInfo["date"]}
);
return Scheduler.Event.NEXT;
}
var instructionClock;
var text;
var key_resp;
var trialClock;
var cam;
var text_2;
var key_resp_2;
var thnksClock;
var text_3;
var globalClock;
var routineTimer;
async function experimentInit() {
// Initialize components for Routine “instruction”
instructionClock = new util.Clock();
text = new visual.TextStim({
win: psychoJS.window,
name: ‘text’,
text: ‘We would like you to record your video consent/assent.\n\n’ +
'If you would like to record your video consent/assent, ’ +
‘please allow camera access when prompted!\n\n’ +
‘To continue, press the space bar.’,
font: ‘Arial’,
units: undefined,
pos: [0, 0], height: 0.06, wrapWidth: 1.4, ori: 0.0,
languageStyle: ‘LTR’,
color: new util.Color(‘black’), opacity: 1.0,
depth: 0.0
});
key_resp = new core.Keyboard({psychoJS: psychoJS, clock: new util.Clock(), waitForStart: true});
// Initialize components for Routine “trial”
trialClock = new util.Clock();
cam = new hardware.Camera({
name:‘cam’,
win: psychoJS.window,});
// Get permission from participant to access their camera
await cam.authorize()
// Switch on cam
await cam.open()
text_2 = new visual.TextStim({
win: psychoJS.window,
name: ‘text_2’,
text: ‘Parents/caregivers, please say: “I consent to my child participating.” \n\n’ +
‘Adolescents, please say: “I agree to participate.”\n\n’ +
‘Press the space bar when you are done.’,
font: ‘Arial’,
units: undefined,
pos: [0, 0], height: 0.06, wrapWidth:1.4, undefined, ori: 0.0,
languageStyle: ‘LTR’,
color: new util.Color(‘black’), opacity: 1,
depth: -1.0
});
key_resp_2 = new core.Keyboard({psychoJS: psychoJS, clock: new util.Clock(), waitForStart: true});
// Initialize components for Routine “thnks”
thnksClock = new util.Clock();
text_3 = new visual.TextStim({
win: psychoJS.window,
name: ‘text_3’,
text: ‘Thank you!’,
font: ‘Arial’,
units: undefined,
pos: [0, 0], height: 0.06, wrapWidth:1.4, undefined, ori: 0.0,
languageStyle: ‘LTR’,
color: new util.Color(‘black’), opacity: 1,
depth: 0.0
});
// Create some handy timers
globalClock = new util.Clock(); // to track the time since experiment started
routineTimer = new util.CountdownTimer(); // to track time remaining of each (non-slip) routine
return Scheduler.Event.NEXT;
}
var t;
var frameN;
var continueRoutine;
var _key_resp_allKeys;
var instructionComponents;
function instructionRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
//--- Prepare to start Routine 'instruction' ---
t = 0;
instructionClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
// update component parameters for each repeat
key_resp.keys = undefined;
key_resp.rt = undefined;
_key_resp_allKeys = [];
// keep track of which components have finished
instructionComponents = [];
instructionComponents.push(text);
instructionComponents.push(key_resp);
for (const thisComponent of instructionComponents)
if ('status' in thisComponent)
thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;
}
}
function instructionRoutineEachFrame() {
return async function () {
//— Loop for each frame of Routine ‘instruction’ —
// get current time
t = instructionClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
// *text* updates
if (t >= 0.0 && text.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
text.tStart = t; // (not accounting for frame time here)
text.frameNStart = frameN; // exact frame index
text.setAutoDraw(true);
}
// *key_resp* updates
if (t >= 0.0 && key_resp.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
key_resp.tStart = t; // (not accounting for frame time here)
key_resp.frameNStart = frameN; // exact frame index
// keyboard checking is just starting
psychoJS.window.callOnFlip(function() { key_resp.clock.reset(); }); // t=0 on next screen flip
psychoJS.window.callOnFlip(function() { key_resp.start(); }); // start on screen flip
psychoJS.window.callOnFlip(function() { key_resp.clearEvents(); });
}
if (key_resp.status === PsychoJS.Status.STARTED) {
let theseKeys = key_resp.getKeys({keyList: ['space'], waitRelease: false});
_key_resp_allKeys = _key_resp_allKeys.concat(theseKeys);
if (_key_resp_allKeys.length > 0) {
key_resp.keys = _key_resp_allKeys[_key_resp_allKeys.length - 1].name; // just the last key pressed
key_resp.rt = _key_resp_allKeys[_key_resp_allKeys.length - 1].rt;
// a response ends the routine
continueRoutine = false;
}
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of instructionComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
// refresh the screen if continuing
if (continueRoutine) {
return Scheduler.Event.FLIP_REPEAT;
} else {
return Scheduler.Event.NEXT;
}
};
}
function instructionRoutineEnd(snapshot) {
return async function () {
//— Ending Routine ‘instruction’ —
for (const thisComponent of instructionComponents) {
if (typeof thisComponent.setAutoDraw === ‘function’) {
thisComponent.setAutoDraw(false);
}
}
// update the trial handler
if (currentLoop instanceof MultiStairHandler) {
currentLoop.addResponse(key_resp.corr, level);
}
psychoJS.experiment.addData(‘key_resp.keys’, key_resp.keys);
if (typeof key_resp.keys !== ‘undefined’) { // we had a response
psychoJS.experiment.addData(‘key_resp.rt’, key_resp.rt);
routineTimer.reset();
}
key_resp.stop();
// the Routine "instruction" was not non-slip safe, so reset the non-slip timer
routineTimer.reset();
// Routines running outside a loop should always advance the datafile row
if (currentLoop === psychoJS.experiment) {
psychoJS.experiment.nextEntry(snapshot);
}
return Scheduler.Event.NEXT;
}
}
var _key_resp_2_allKeys;
var trialComponents;
function trialRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
//--- Prepare to start Routine 'trial' ---
t = 0;
trialClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
// update component parameters for each repeat
key_resp_2.keys = undefined;
key_resp_2.rt = undefined;
_key_resp_2_allKeys = [];
// keep track of which components have finished
trialComponents = [];
trialComponents.push(cam);
trialComponents.push(text_2);
trialComponents.push(key_resp_2);
for (const thisComponent of trialComponents)
if ('status' in thisComponent)
thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;
}
}
var frameRemains;
function trialRoutineEachFrame() {
return async function () {
//— Loop for each frame of Routine ‘trial’ —
// get current time
t = trialClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
if (t >= 0 && cam.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
cam.tStart = t; // (not accounting for frame time here)
cam.frameNStart = frameN; // exact frame index
await cam.record()
};
frameRemains = 0 + - psychoJS.window.monitorFramePeriod * 0.75; // most of one frame period left
if (cam.status === PsychoJS.Status.STARTED && t >= frameRemains) {
await cam.stop()
};
// *text_2* updates
if (t >= 0.0 && text_2.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
text_2.tStart = t; // (not accounting for frame time here)
text_2.frameNStart = frameN; // exact frame index
text_2.setAutoDraw(true);
}
// *key_resp_2* updates
if (t >= 0.0 && key_resp_2.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
key_resp_2.tStart = t; // (not accounting for frame time here)
key_resp_2.frameNStart = frameN; // exact frame index
// keyboard checking is just starting
psychoJS.window.callOnFlip(function() { key_resp_2.clock.reset(); }); // t=0 on next screen flip
psychoJS.window.callOnFlip(function() { key_resp_2.start(); }); // start on screen flip
psychoJS.window.callOnFlip(function() { key_resp_2.clearEvents(); });
}
if (key_resp_2.status === PsychoJS.Status.STARTED) {
let theseKeys = key_resp_2.getKeys({keyList: ['space'], waitRelease: false});
_key_resp_2_allKeys = _key_resp_2_allKeys.concat(theseKeys);
if (_key_resp_2_allKeys.length > 0) {
key_resp_2.keys = _key_resp_2_allKeys[_key_resp_2_allKeys.length - 1].name; // just the last key pressed
key_resp_2.rt = _key_resp_2_allKeys[_key_resp_2_allKeys.length - 1].rt;
// a response ends the routine
continueRoutine = false;
}
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of trialComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
// refresh the screen if continuing
if (continueRoutine) {
return Scheduler.Event.FLIP_REPEAT;
} else {
return Scheduler.Event.NEXT;
}
};
}
function trialRoutineEnd(snapshot) {
return async function () {
//— Ending Routine ‘trial’ —
for (const thisComponent of trialComponents) {
if (typeof thisComponent.setAutoDraw === ‘function’) {
thisComponent.setAutoDraw(false);
}
}
// Ensure that cam is stopped
if (cam.status === PsychoJS.Status.STARTED) {
await cam.stop()
}
// Save cam recording
let camFilename = recording_cam_${util.MonotonicClock.getDateStr()}
;
await cam.save({
tag: camFilename,
waitForCompletion: true,
showDialog: true,
dialogMsg: “Please wait a few moments while the video is uploading to the server…”
});
psychoJS.experiment.addData(‘cam.clip’, camFilename);
// update the trial handler
if (currentLoop instanceof MultiStairHandler) {
currentLoop.addResponse(key_resp_2.corr, level);
}
psychoJS.experiment.addData(‘key_resp_2.keys’, key_resp_2.keys);
if (typeof key_resp_2.keys !== ‘undefined’) { // we had a response
psychoJS.experiment.addData(‘key_resp_2.rt’, key_resp_2.rt);
routineTimer.reset();
}
key_resp_2.stop();
// the Routine "trial" was not non-slip safe, so reset the non-slip timer
routineTimer.reset();
// Routines running outside a loop should always advance the datafile row
if (currentLoop === psychoJS.experiment) {
psychoJS.experiment.nextEntry(snapshot);
}
return Scheduler.Event.NEXT;
}
}
var thnksComponents;
function thnksRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
//--- Prepare to start Routine 'thnks' ---
t = 0;
thnksClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
routineTimer.add(3.000000);
// update component parameters for each repeat
// keep track of which components have finished
thnksComponents = [];
thnksComponents.push(text_3);
for (const thisComponent of thnksComponents)
if ('status' in thisComponent)
thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;
}
}
function thnksRoutineEachFrame() {
return async function () {
//— Loop for each frame of Routine ‘thnks’ —
// get current time
t = thnksClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
// *text_3* updates
if (t >= 0.0 && text_3.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
text_3.tStart = t; // (not accounting for frame time here)
text_3.frameNStart = frameN; // exact frame index
text_3.setAutoDraw(true);
}
frameRemains = 0.0 + 1.0 - psychoJS.window.monitorFramePeriod * 0.75; // most of one frame period left
if (text_3.status === PsychoJS.Status.STARTED && t >= frameRemains) {
text_3.setAutoDraw(false);
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of thnksComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
// refresh the screen if continuing
if (continueRoutine && routineTimer.getTime() > 0) {
return Scheduler.Event.FLIP_REPEAT;
} else {
return Scheduler.Event.NEXT;
}
};
}
function thnksRoutineEnd(snapshot) {
return async function () {
//— Ending Routine ‘thnks’ —
for (const thisComponent of thnksComponents) {
if (typeof thisComponent.setAutoDraw === ‘function’) {
thisComponent.setAutoDraw(false);
}
}
// Routines running outside a loop should always advance the datafile row
if (currentLoop === psychoJS.experiment) {
psychoJS.experiment.nextEntry(snapshot);
}
return Scheduler.Event.NEXT;
}
}
function importConditions(currentLoop) {
return async function () {
psychoJS.importAttributes(currentLoop.getCurrentTrial());
return Scheduler.Event.NEXT;
};
}
async function quitPsychoJS(message, isCompleted) {
// Check for and save orphaned data
if (psychoJS.experiment.isEntryEmpty()) {
psychoJS.experiment.nextEntry();
}
psychoJS.window.close();
psychoJS.quit({message: message, isCompleted: isCompleted});
return Scheduler.Event.QUIT;
}