added timer to survey

This commit is contained in:
Shaheed Azaad
2025-07-02 00:10:11 +02:00
parent 34239413b1
commit 9460d6247c
7 changed files with 1111 additions and 83 deletions

View File

@@ -1,6 +0,0 @@
VITE_DEBUG=true
VITE_COMPLETE_URL=
VITE_CLOSED_URL=
VITE_EXPERIMENT_TITLE=
VITE_EXPERIMENT_NAME=
VITE_DEPLOY_URL=

View File

@@ -1,6 +0,0 @@
VITE_DEBUG=false
VITE_COMPLETE_URL=
VITE_CLOSED_URL=
VITE_EXPERIMENT_TITLE=
VITE_EXPERIMENT_NAME=
VITE_DEPLOY_URL=

180
index.js
View File

@@ -10,16 +10,18 @@ import '@jspsych/plugin-survey/css/survey.css';
import './styles.css'; import './styles.css';
import { getStimulusMap } from './scripts/text-stimuli.js'; import { getStimulusMap } from './scripts/text-stimuli.js';
import jsPsychObjectMoving from './scripts/plugin-object-moving.js'; import jsPsychObjectMoving from './scripts/plugin-object-moving.js';
import { textStimuli } from './scripts/text-stimuli.js';
const debug = import.meta.env.VITE_DEBUG === 'true'; const total_participants = import.meta.env.VITE_TOTAL_PARTICIPANTS || 2;
const total_participants = import.meta.env.VITE_TOTAL_PARTICIPANTS;
const uniqueUsernames = generateUniqueUsernames(total_participants); const uniqueUsernames = generateUniqueUsernames(total_participants);
const experiment_name = import.meta.env.VITE_EXPERIMENT_NAME; const experiment_name = import.meta.env.VITE_EXPERIMENT_NAME;
let prolific_id; let prolific_id;
let probe_condition; // P in the params, O = open, S = suspicion mentioned let probe_condition; // P in the params, O = open, S = suspicion mentioned
let mapping; // M in the params, TB = together blue, AB = alone blue let debug = false;
let short_version = false;
const short_version = true; // just using the short version of the task
function delayed_redirect(url) { function delayed_redirect(url) {
setTimeout(() => { setTimeout(() => {
@@ -28,51 +30,68 @@ function delayed_redirect(url) {
} }
const jsPsych = initJsPsych({ const jsPsych = initJsPsych({
on_finish: function() { on_finish: function () {
jsPsych.getDisplayElement().innerHTML = textStimuli.complete; jsPsych.getDisplayElement().innerHTML = textStimuli.complete;
}, },
on_close: function() { on_close: function () {
delayed_redirect(import.meta.env.VITE_CLOSED_URL); delayed_redirect(import.meta.env.VITE_CLOSED_URL);
}, },
on_data_update: function() { on_data_update: function () {
if (debug) { if (debug) {
console.log(jsPsych.data.get().json()); console.log(jsPsych.data.get().json());
} }
}, },
}); });
prolific_id = jsPsych.data.getURLVariable('PROLIFIC_PID'); debug = jsPsych.data.getURLVariable('debug') === 'true';
mapping = jsPsych.data.getURLVariable('M');
short_version = jsPsych.data.getURLVariable('S') === 'true';
const together_colour = mapping === 'TB' ? 'blue' : 'red';
const stimulusMap = getStimulusMap(together_colour);
probe_condition = jsPsych.data.getURLVariable('P'); prolific_id = jsPsych.data.getURLVariable('PROLIFIC_PID');
const COND = Number(jsPsych.data.getURLVariable('C'));
const probe_text_direct = 'Did you have any thoughts, observations, or suspicions about the experiment?'
const probe_text_indirect = 'Did you have any thoughts or observations about the experiment?';
let together_colour;
let probe_text; let probe_text;
if (probe_condition === 'O') {
probe_text =
'Did you have any thoughts or observations about the experiment?'; switch (COND) {
} else if (probe_condition === 'S') { case 0:
probe_text = probe_condition = 'direct';
'Did you have any thoughts, observations, or suspicions about the experiment?'; together_colour = 'blue';
} else if (probe_condition === 'D') { break;
probe_text = case 1:
'Many studies use deception to create the appearance that you are interacting with a real person. Did you suspect that you were not interacting with a real person?'; probe_condition = 'direct';
} else if (probe_condition === 'R') { together_colour = 'red';
probe_text = break;
'Our study used deception to create the appearance that you are interacting with a real person. Did you suspect that you were not interacting with a real person?'; case 2:
} else { probe_condition = 'indirect';
probe_text = together_colour = 'blue';
'Did you have any thoughts or observations about the experiment?'; break;
case 3:
probe_condition = 'indirect';
together_colour = 'red';
break;
} }
jsPsych.data.addProperties({ probe_text = probe_condition === 'direct' ? probe_text_direct : probe_text_indirect;
const stimulusMap = getStimulusMap(together_colour);
const props = {
condition: probe_condition, condition: probe_condition,
together_colour: together_colour, together_colour: together_colour,
prolific_id: prolific_id, prolific_id: prolific_id,
experiment_name: experiment_name, experiment_name: experiment_name,
probe_text: probe_text, probe_text: probe_text,
}); }
if (debug) {
console.log(props);
}
jsPsych.data.addProperties(props);
const timeline = []; const timeline = [];
@@ -91,7 +110,7 @@ const consent_form = {
type: jsPsychHtmlButtonResponse, type: jsPsychHtmlButtonResponse,
stimulus: stimulusMap.get('consent'), stimulus: stimulusMap.get('consent'),
choices: ['Exit', 'Continue'], choices: ['Exit', 'Continue'],
on_finish: function(data) { on_finish: function (data) {
if (data.response === 0) { if (data.response === 0) {
jsPsych.abortExperiment(stimulusMap.get('no_consent')); jsPsych.abortExperiment(stimulusMap.get('no_consent'));
} }
@@ -176,8 +195,39 @@ const debrief = {
stimulus: stimulusMap.get('debrief'), stimulus: stimulusMap.get('debrief'),
}; };
const pre_survey_info = {
type: jsPsychHtmlKeyboardResponse,
choices: [' '],
stimulus: stimulusMap.get('pre_survey_info'),
};
const survey_function = (survey) => {
survey.onAfterRenderPage.add(function (sender, options) {
if (survey.activePage.name === 'page1') {
const nextButton = document.querySelector('#sv-nav-next > div > input');
if (nextButton) {
let seconds = 30;
const originalText = nextButton.value.replace(/\s*\(\d+\)$/, '') || 'Continue';
nextButton.disabled = true;
nextButton.value = `${originalText} (${seconds})`;
const interval = setInterval(() => {
seconds--;
nextButton.value = `${originalText} (${seconds})`;
if (seconds <= 0) {
clearInterval(interval);
nextButton.disabled = false;
nextButton.value = originalText;
}
}, 1000);
}
console.log('Survey page rendered:', sender.currentPage);
}
});
}
const survey = { const survey = {
type: jsPsychSurvey, type: jsPsychSurvey,
survey_function: survey_function,
survey_json: { survey_json: {
showQuestionNumbers: false, showQuestionNumbers: false,
completeText: 'Done!', completeText: 'Done!',
@@ -187,48 +237,18 @@ const survey = {
pages: [ pages: [
{ {
name: 'page1', name: 'page1',
elements: [
{
type: 'radiogroup',
title: 'Please indicate your gender',
choices: ['Male', 'Female', 'Other'],
isRequired: debug ? false : true,
colCount: 0,
name: 'gender',
},
{
type: 'radiogroup',
title: 'Please indicate your handedness',
choices: ['Right', 'Left', 'Ambidextrous/Other'],
isRequired: debug ? false : true,
colCount: 0,
name: 'handedness',
},
{
type: 'text',
title: 'How old are you?',
name: 'age',
isRequired: debug ? false : false,
inputType: 'number',
min: 18,
max: 100,
defaultValue: 18,
},
],
},
{
name: 'page2',
elements: [ elements: [
{ {
type: 'comment', type: 'comment',
title: probe_text, title: probe_text,
description: 'You may continue to the next page after 30 seconds.',
name: 'probe', name: 'probe',
isRequired: debug ? false : true, isRequired: debug ? false : true,
}, },
], ],
}, },
{ {
name: 'page3', name: 'page2',
elements: [ elements: [
{ {
type: 'matrix', type: 'matrix',
@@ -271,6 +291,37 @@ const survey = {
}, },
], ],
}, },
{
name: 'page1',
elements: [
{
type: 'radiogroup',
title: 'Please indicate your gender',
choices: ['Male', 'Female', 'Other'],
isRequired: debug ? false : true,
colCount: 0,
name: 'gender',
},
{
type: 'radiogroup',
title: 'Please indicate your handedness',
choices: ['Right', 'Left', 'Ambidextrous/Other'],
isRequired: debug ? false : true,
colCount: 0,
name: 'handedness',
},
{
type: 'text',
title: 'How old are you?',
name: 'age',
isRequired: debug ? false : false,
inputType: 'number',
min: 18,
max: 100,
defaultValue: 18,
},
],
}
], ],
}, },
}; };
@@ -323,8 +374,8 @@ const object_moving_practice = {
}; };
if (debug) { if (debug) {
timeline.push(survey);
timeline.push(debrief); timeline.push(debrief);
} }
if (!debug) { if (!debug) {
@@ -343,6 +394,7 @@ if (!debug) {
timeline.push(pre_task_instructions); timeline.push(pre_task_instructions);
timeline.push(lobby_fast); timeline.push(lobby_fast);
timeline.push(object_moving_trials); timeline.push(object_moving_trials);
timeline.push(pre_survey_info);
timeline.push(survey); timeline.push(survey);
timeline.push(debrief); timeline.push(debrief);
} }

961
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,8 +14,13 @@
"vite": "^6.3.1" "vite": "^6.3.1"
}, },
"dependencies": { "dependencies": {
"@jspsych/plugin-fullscreen": "^2.1.0",
"@jspsych/plugin-html-button-response": "^2.1.0",
"@jspsych/plugin-html-keyboard-response": "^2.1.0", "@jspsych/plugin-html-keyboard-response": "^2.1.0",
"@jspsych/plugin-survey": "^2.1.0",
"@tailwindcss/vite": "^4.1.4", "@tailwindcss/vite": "^4.1.4",
"archiver": "^7.0.1",
"dotenv": "^17.0.1",
"jspsych": "^8.2.1", "jspsych": "^8.2.1",
"prettier-plugin-html-template-literals": "^1.0.5", "prettier-plugin-html-template-literals": "^1.0.5",
"tailwindcss": "^4.1.4", "tailwindcss": "^4.1.4",

View File

@@ -160,9 +160,9 @@ class jsPsychObjectMoving {
} }
function initiate_partner_action(){ function initiate_partner_action(){
const go_delay = trial.selector === 'participant' ? 1200 : 800 const go_delay = trial.selector === 'participant' ? 1400 : 1000
const partner_go_delay = jsPsych.randomization.sampleExGaussian(go_delay, 300, 1/100, true); const partner_go_delay = jsPsych.randomization.sampleExGaussian(go_delay, 500, 1/100, true);
const partner_teleport_delay = jsPsych.randomization.sampleExGaussian(1200, 200, 1/100, true); const partner_teleport_delay = jsPsych.randomization.sampleExGaussian(1400, 300, 1/100, true);
setTimeout(() => { setTimeout(() => {
change_object_colour(object_partner); change_object_colour(object_partner);

View File

@@ -15,10 +15,15 @@ const redImages = {
instructions_4: '/images/red/instructions_4.png', instructions_4: '/images/red/instructions_4.png',
}; };
const html = (strings, ...values) => import html from '../utils/html.js';
strings
.reduce((result, str, i) => result + str + (values[i] || ''), '') export const textStimuli = {
.trim(); complete: html`Experiment complete. Please paste the following link into your browser to confirm completion on Prolific:
<span class="text-blue-500">
${import.meta.env.VITE_COMPLETE_URL}
</a>
`,
};
function getStimulusMap(together_colour) { function getStimulusMap(together_colour) {
const colourMap = getColourMap(together_colour); const colourMap = getColourMap(together_colour);
@@ -28,6 +33,23 @@ function getStimulusMap(together_colour) {
// Select the appropriate image map based on the color // Select the appropriate image map based on the color
const imageMap = together_colour === 'blue' ? blueImages : redImages; const imageMap = together_colour === 'blue' ? blueImages : redImages;
stimulusMap.set('pre_survey_info', html`
<p class="leading-relaxed">
You will now answer a few questions about your experience in the experiment. It is important that you read the questions carefully and answer them honestly.
</p>
<p class ="leading-relaxed mt-2">
Nonsense or random answers may lead to your submission being rejected.
</p>
<p class="mt-6">
Press
<strong>SPACE</strong>
to continue.
</p>
`
);
stimulusMap.set( stimulusMap.set(
'pre_consent_info', 'pre_consent_info',
html` html`