From 9b226d60da219074a02788d33616caf4092170e0 Mon Sep 17 00:00:00 2001
From: Shaheed Azaad
Date: Fri, 20 Feb 2026 00:49:20 +0100
Subject: [PATCH] working prototype
---
index.js | 92 ++++++++++-
plugins/jspsych-captcha.js | 303 ++++++++++++++++++++++++++++++++++++
plugins/jspsych-die-roll.js | 209 +++++++++++++++++++++++++
scripts/text-stimuli.js | 114 ++++++++++----
styles.css | 127 ++++++++++++++-
5 files changed, 810 insertions(+), 35 deletions(-)
create mode 100644 plugins/jspsych-captcha.js
create mode 100644 plugins/jspsych-die-roll.js
diff --git a/index.js b/index.js
index 33bda1e..55d61de 100644
--- a/index.js
+++ b/index.js
@@ -8,13 +8,17 @@ import '@jspsych/plugin-survey/css/survey.css';
import './styles.css';
import { getStimulusMap } from './scripts/text-stimuli.js';
import { textStimuli } from './scripts/text-stimuli.js';
+import JsPsychDieRoll from './plugins/jspsych-die-roll.js';
+import JsPsychCaptcha from './plugins/jspsych-captcha.js';
+import html from './utils/html.js';
const experiment_name = import.meta.env.VITE_EXPERIMENT_NAME;
let prolific_id;
let probe_condition; // will be set to ai or human based on the condition.
-let debug = false;
+let debug;
let probe_order; // will be set to ai_first or human_first based on the condition
+const CAPTCHA_TRIAL_COUNT = debug ? 2 : 12; // set to 10 for main experiment
function delayed_redirect(url) {
setTimeout(() => {
@@ -34,9 +38,14 @@ const jsPsych = initJsPsych({
console.log(jsPsych.data.get().json());
}
},
+ show_progress_bar: true,
});
-debug = jsPsych.data.getURLVariable('debug') === 'true';
+debug =
+ jsPsych.data.getURLVariable('debug') === 'true' ||
+ import.meta.env.VITE_DEBUG === 'true';
+
+console.log('debug:', debug);
prolific_id = jsPsych.data.getURLVariable('PROLIFIC_PID');
@@ -54,22 +63,28 @@ const probe_text_difficulty =
'while the die roll was random, the difficulty of the captcha task was pre-determined and unrelated to the die roll. ' +
probe_closing_text;
+let die_result = 3;
+
switch (COND) {
case 0:
probe_condition = 'die';
probe_order = 'die_first';
+ die_result = 4;
break;
case 1:
probe_condition = 'die';
probe_order = 'die_first';
+ die_result = 4;
break;
case 2:
probe_condition = 'difficulty';
probe_order = 'die_first';
+ die_result = 4;
break;
case 3:
probe_condition = 'difficulty';
probe_order = 'die_first';
+ die_result = 4;
break;
case 4:
probe_condition = 'die';
@@ -135,6 +150,18 @@ const instructions_1 = {
stimulus: stimulusMap.get('instructions_1'),
};
+const instructions_2 = {
+ type: jsPsychHtmlKeyboardResponse,
+ choices: [' '],
+ stimulus: stimulusMap.get('instructions_2'),
+};
+
+const instructions_3 = {
+ type: jsPsychHtmlKeyboardResponse,
+ choices: [' '],
+ stimulus: stimulusMap.get('instructions_3'),
+};
+
const debrief = {
type: jsPsychHtmlKeyboardResponse,
choices: [' '],
@@ -147,6 +174,40 @@ const pre_survey_info = {
stimulus: stimulusMap.get('pre_survey_info'),
};
+const die_roll_trial = {
+ type: JsPsychDieRoll,
+ prompt: html`
+
+ Click the die once to start it rolling, then click it again to stop.
+
+ `,
+ rigged_value: die_result,
+ roll_duration: 2200,
+ result_template: `You rolled a {{value}} (moderate difficulty). Click 'continue' to proceed.`,
+};
+
+const create_captcha_trial = trial_index => {
+ return {
+ type: JsPsychCaptcha,
+ prompt:
+ 'Enter the characters you see into the textbox below.
',
+ difficulty: die_result,
+ button_label: 'Continue',
+ error_text: '',
+ allow_refresh: false,
+ require_correct: false,
+ data: {
+ trial_id: 'captcha_entry',
+ captcha_index: trial_index + 1,
+ captcha_total: CAPTCHA_TRIAL_COUNT,
+ },
+ };
+};
+
+const captcha_trials = Array.from({ length: CAPTCHA_TRIAL_COUNT }, (_, index) =>
+ create_captcha_trial(index)
+);
+
const die_probe_row = {
text:
'I suspected that the die roll was not random, or the number I received was pre-determined.',
@@ -209,6 +270,14 @@ const survey = {
text: `I found the captcha task difficult.`,
value: 'Difficulty',
},
+ {
+ text: `I felt lucky during the die roll.`,
+ value: 'Luck',
+ },
+ {
+ text: `I felt unlucky during the die roll.`,
+ value: 'Bad_luck',
+ },
{
text: `I think I solved all the captchas correctly.`,
value: 'Accuracy',
@@ -331,18 +400,25 @@ const survey = {
},
};
+const main_experiment_timeline = [
+ instructions_1,
+ instructions_2,
+ die_roll_trial,
+ instructions_3,
+ ...captcha_trials,
+ pre_survey_info,
+ survey,
+ debrief,
+];
+
if (debug) {
- timeline.push(survey);
- timeline.push(debrief);
+ timeline.push(...main_experiment_timeline);
}
if (!debug) {
timeline.push(pre_consent_info);
timeline.push(consent_form);
timeline.push(enter_fullscreen);
- timeline.push(instructions_1);
- timeline.push(pre_survey_info);
- timeline.push(survey);
- timeline.push(debrief);
+ timeline.push(...main_experiment_timeline);
}
jsPsych.run(timeline);
diff --git a/plugins/jspsych-captcha.js b/plugins/jspsych-captcha.js
new file mode 100644
index 0000000..9d1a25d
--- /dev/null
+++ b/plugins/jspsych-captcha.js
@@ -0,0 +1,303 @@
+import { ParameterType } from 'jspsych';
+
+const info = {
+ name: 'captcha',
+ parameters: {
+ prompt: {
+ type: ParameterType.HTML_STRING,
+ default: '',
+ },
+ difficulty: {
+ type: ParameterType.INT,
+ default: 3,
+ },
+ button_label: {
+ type: ParameterType.STRING,
+ default: 'Submit',
+ },
+ allow_refresh: {
+ type: ParameterType.BOOL,
+ default: true,
+ },
+ require_correct: {
+ type: ParameterType.BOOL,
+ default: true,
+ },
+ error_text: {
+ type: ParameterType.HTML_STRING,
+ default: 'That entry did not match, please try again.',
+ },
+ },
+};
+
+class CaptchaPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ this.characters = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
+ }
+
+ trial(display_element, trial) {
+ const start_time = performance.now();
+ const attempt_counts = { submission_count: 0, refresh_count: 0 };
+ const difficulty_settings = this.resolve_difficulty(trial.difficulty);
+ let captcha_text = this.generate_text(difficulty_settings.length);
+ let last_target_at_submission = captcha_text;
+
+ display_element.innerHTML = this.build_markup(
+ trial.prompt,
+ trial.button_label,
+ trial.allow_refresh
+ );
+
+ const canvas_element = display_element.querySelector(
+ '.jspsych_captcha_canvas'
+ );
+ const canvas_context = canvas_element.getContext('2d');
+ const input_element = display_element.querySelector(
+ '.jspsych_captcha_input'
+ );
+ const button_element = display_element.querySelector(
+ '.jspsych_captcha_submit'
+ );
+ const refresh_button = display_element.querySelector(
+ '.jspsych_captcha_refresh'
+ );
+ const error_element = display_element.querySelector(
+ '.jspsych_captcha_error'
+ );
+
+ const draw_captcha = () => {
+ this.draw_captcha(canvas_context, captcha_text, difficulty_settings);
+ };
+
+ draw_captcha();
+ input_element.focus();
+
+ if (refresh_button) {
+ refresh_button.addEventListener('click', () => {
+ attempt_counts.refresh_count += 1;
+ captcha_text = this.generate_text(difficulty_settings.length);
+ draw_captcha();
+ error_element.textContent = '';
+ input_element.value = '';
+ input_element.focus();
+ });
+ }
+
+ const length_hint_element = display_element.querySelector(
+ '.jspsych_captcha_length_hint'
+ );
+
+ const update_button_state = () => {
+ const current_length = input_element.value.trim().length;
+ const required_length = captcha_text.length;
+ button_element.disabled = current_length < required_length;
+ if (length_hint_element) {
+ length_hint_element.textContent =
+ current_length < required_length
+ ? `Type ${required_length - current_length} more character(s) to continue.`
+ : '';
+ }
+ };
+
+ const finish_trial = (response_value, is_correct) => {
+ const trial_data = {
+ rt: performance.now() - start_time,
+ response: response_value,
+ participant_entry: response_value,
+ participant_entry_length: response_value.length,
+ captcha_target: last_target_at_submission,
+ correct: is_correct,
+ submission_count: attempt_counts.submission_count,
+ manual_refreshes: attempt_counts.refresh_count,
+ difficulty_label: difficulty_settings.label,
+ difficulty_length: difficulty_settings.length,
+ difficulty_level: difficulty_settings.level,
+ };
+ display_element.innerHTML = '';
+ this.jsPsych.finishTrial(trial_data);
+ };
+
+ const validate_response = () => {
+ attempt_counts.submission_count += 1;
+ const response_value = input_element.value.trim().toUpperCase();
+ last_target_at_submission = captcha_text;
+ const is_correct = response_value === captcha_text;
+
+ if (response_value.length < captcha_text.length) {
+ error_element.textContent = `Please enter all ${captcha_text.length} characters shown in the captcha.`;
+ update_button_state();
+ return;
+ }
+
+ if (is_correct || !trial.require_correct) {
+ finish_trial(response_value, is_correct);
+ return;
+ }
+
+ error_element.textContent = trial.error_text;
+ captcha_text = this.generate_text(difficulty_settings.length);
+ draw_captcha();
+ input_element.value = '';
+ input_element.focus();
+ };
+
+ button_element.addEventListener('click', validate_response);
+ input_element.addEventListener('keydown', event => {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ validate_response();
+ }
+ });
+ input_element.addEventListener('input', update_button_state);
+ update_button_state();
+ }
+
+ build_markup(prompt, button_label, allow_refresh) {
+ const prompt_html = prompt
+ ? `${prompt}
`
+ : '';
+ const refresh_button = allow_refresh
+ ? '↻ '
+ : '';
+
+ return `
+
+ ${prompt_html}
+
+
+ ${refresh_button}
+
+
+
+
+
+
${button_label}
+
+
+ `;
+ }
+
+ draw_captcha(canvas_context, captcha_text, settings) {
+ const { width, height } = canvas_context.canvas;
+ canvas_context.clearRect(0, 0, width, height);
+ const gradient = canvas_context.createLinearGradient(0, 0, width, height);
+ gradient.addColorStop(0, '#f8fafc');
+ gradient.addColorStop(1, '#e2e8f0');
+ canvas_context.fillStyle = gradient;
+ canvas_context.fillRect(0, 0, width, height);
+
+ for (let index = 0; index < settings.noise_lines; index++) {
+ canvas_context.beginPath();
+ canvas_context.moveTo(Math.random() * width, Math.random() * height);
+ canvas_context.bezierCurveTo(
+ Math.random() * width,
+ Math.random() * height,
+ Math.random() * width,
+ Math.random() * height,
+ Math.random() * width,
+ Math.random() * height
+ );
+ canvas_context.lineWidth = 1 + Math.random() * 2;
+ canvas_context.strokeStyle = `rgba(30, 41, 59, ${0.15 +
+ Math.random() * 0.2})`;
+ canvas_context.stroke();
+ }
+
+ canvas_context.font = `bold ${settings.font_size}px 'Courier New', monospace`;
+ canvas_context.textBaseline = 'middle';
+ canvas_context.textAlign = 'center';
+
+ const horizontal_step = width / (captcha_text.length + 1);
+ const center_y = height / 2;
+
+ for (let index = 0; index < captcha_text.length; index++) {
+ const current_character = captcha_text[index];
+ canvas_context.save();
+ const jitter_y = this.random_between(-settings.jitter, settings.jitter);
+ canvas_context.translate(
+ horizontal_step * (index + 1),
+ center_y + jitter_y
+ );
+ canvas_context.rotate(
+ this.random_between(-settings.rotation, settings.rotation)
+ );
+ canvas_context.fillStyle = '#000000';
+ canvas_context.fillText(current_character, 0, 0);
+ canvas_context.restore();
+ }
+
+ const noise_pixels = Math.floor(width * height * settings.speckle_density);
+ const image_data = canvas_context.getImageData(0, 0, width, height);
+ for (let index = 0; index < noise_pixels; index++) {
+ const pixel_offset =
+ (Math.floor(Math.random() * width) +
+ Math.floor(Math.random() * height) * width) *
+ 4;
+ const shade = Math.random() > 0.5 ? 255 : 0;
+ image_data.data[pixel_offset] = shade;
+ image_data.data[pixel_offset + 1] = shade;
+ image_data.data[pixel_offset + 2] = shade;
+ image_data.data[pixel_offset + 3] = 255;
+ }
+ canvas_context.putImageData(image_data, 0, 0);
+ }
+
+ generate_text(length) {
+ let generated_text = '';
+ for (let index = 0; index < length; index++) {
+ generated_text += this.characters.charAt(
+ Math.floor(Math.random() * this.characters.length)
+ );
+ }
+ return generated_text;
+ }
+
+ resolve_difficulty(difficulty_input) {
+ const default_level = 3;
+ let normalized_level = Number(difficulty_input);
+ if (!Number.isFinite(normalized_level)) {
+ normalized_level = default_level;
+ }
+ normalized_level = Math.round(Math.min(6, Math.max(1, normalized_level)));
+
+ const base_length = 7;
+ const base_font_size = 20;
+ const base_rotation = 0.25;
+ const max_rotation = base_rotation * 3;
+ const base_noise_lines = 4;
+ const base_speckle_density = 0.01;
+ const base_jitter = 25;
+ const noise_line_step = 0.8;
+ const speckle_step = 0.0015;
+ const jitter_step = 1.6;
+ const rotation_step = (max_rotation - base_rotation) / 5;
+
+ const rotation = base_rotation + rotation_step * (normalized_level - 1);
+ const noise_lines = Math.round(
+ base_noise_lines + noise_line_step * (normalized_level - 1)
+ );
+ const speckle_density =
+ base_speckle_density + speckle_step * (normalized_level - 1);
+ const jitter = base_jitter + jitter_step * (normalized_level - 1);
+
+ return {
+ length: base_length,
+ noise_lines: noise_lines,
+ rotation: rotation,
+ jitter: jitter,
+ font_size: base_font_size,
+ speckle_density: speckle_density,
+ label: `level_${normalized_level}`,
+ level: normalized_level,
+ };
+ }
+
+ random_between(min, max) {
+ return Math.random() * (max - min) + min;
+ }
+}
+
+CaptchaPlugin.info = info;
+
+export default CaptchaPlugin;
diff --git a/plugins/jspsych-die-roll.js b/plugins/jspsych-die-roll.js
new file mode 100644
index 0000000..d6202fa
--- /dev/null
+++ b/plugins/jspsych-die-roll.js
@@ -0,0 +1,209 @@
+import { ParameterType } from 'jspsych';
+
+const info = {
+ name: 'die-roll',
+ parameters: {
+ prompt: {
+ type: ParameterType.HTML_STRING,
+ default: '',
+ },
+ rigged_value: {
+ type: ParameterType.INT,
+ default: null,
+ },
+ roll_duration: {
+ type: ParameterType.INT,
+ default: 2000,
+ },
+ animation_interval: {
+ type: ParameterType.INT,
+ default: 120,
+ },
+ button_label: {
+ type: ParameterType.STRING,
+ default: 'Continue',
+ },
+ result_template: {
+ type: ParameterType.STRING,
+ default: 'You rolled a {{value}}.',
+ },
+ },
+};
+
+class DieRollPlugin {
+ constructor(jsPsych) {
+ this.jsPsych = jsPsych;
+ }
+
+ trial(display_element, trial) {
+ const start_time = performance.now();
+ const sanitized_rigged_value = this.get_rigged_value(trial.rigged_value);
+ const final_value =
+ sanitized_rigged_value ??
+ this.jsPsych.randomization.sampleWithoutReplacement(
+ [1, 2, 3, 4, 5, 6],
+ 1
+ )[0];
+
+ display_element.innerHTML = this.build_markup(
+ trial.prompt,
+ trial.button_label
+ );
+
+ const face_element = display_element.querySelector(
+ '.jspsych_die_roll_face'
+ );
+ const result_element = display_element.querySelector(
+ '.jspsych_die_roll_result'
+ );
+ const button_element = display_element.querySelector(
+ '.jspsych_die_roll_button'
+ );
+
+ let is_rolling = false;
+ let has_rolled = false;
+ let roll_started_at = null;
+ let roll_stopped_at = null;
+ let die_click_count = 0;
+
+ const clear_timeouts = () => {
+ this.jsPsych.pluginAPI.clearAllTimeouts();
+ };
+
+ const cycle_face = () => {
+ if (!is_rolling) {
+ return;
+ }
+ const value = this.random_face(final_value);
+ face_element.textContent = value;
+ this.jsPsych.pluginAPI.setTimeout(cycle_face, trial.animation_interval);
+ };
+
+ const finalize_roll = () => {
+ if (!is_rolling || has_rolled) {
+ return;
+ }
+ is_rolling = false;
+ has_rolled = true;
+ die_click_count += 1;
+ roll_stopped_at = performance.now();
+ clear_timeouts();
+ face_element.textContent = final_value;
+ face_element.classList.remove('jspsych_die_roll_face--active');
+ face_element.classList.remove('jspsych_die_roll_face--interactive');
+ face_element.classList.add('jspsych_die_roll_face--locked');
+ face_element.setAttribute('aria-disabled', 'true');
+ face_element.removeAttribute('tabindex');
+ face_element.style.pointerEvents = 'none';
+ result_element.textContent = trial.result_template.replace(
+ /\{\{value\}\}/gi,
+ final_value
+ );
+ button_element.disabled = false;
+ button_element.focus();
+ };
+
+ const begin_roll = () => {
+ if (is_rolling || has_rolled) {
+ return;
+ }
+ is_rolling = true;
+ die_click_count += 1;
+ roll_started_at = performance.now();
+ result_element.textContent = 'Rolling... click again to stop the die.';
+ face_element.classList.add('jspsych_die_roll_face--active');
+ button_element.disabled = true;
+ clear_timeouts();
+ cycle_face();
+ };
+
+ const handle_face_interaction = () => {
+ if (!is_rolling && !has_rolled) {
+ begin_roll();
+ } else if (is_rolling && !has_rolled) {
+ finalize_roll();
+ }
+ };
+
+ button_element.disabled = true;
+ face_element.textContent = '';
+ result_element.textContent =
+ 'Click the die to start, then click it again to lock in your number.';
+ face_element.classList.add('jspsych_die_roll_face--interactive');
+ face_element.setAttribute('tabindex', '0');
+ face_element.setAttribute('role', 'button');
+ face_element.setAttribute(
+ 'aria-label',
+ 'Virtual die. Click to start and stop.'
+ );
+
+ face_element.addEventListener('click', handle_face_interaction);
+ face_element.addEventListener('keydown', event => {
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault();
+ handle_face_interaction();
+ }
+ });
+
+ const end_trial = () => {
+ is_rolling = false;
+ clear_timeouts();
+ const trial_data = {
+ roll: final_value,
+ rigged_value: sanitized_rigged_value,
+ rigged: sanitized_rigged_value !== null,
+ die_click_count: die_click_count,
+ roll_started_after_ms: roll_started_at
+ ? roll_started_at - start_time
+ : null,
+ roll_duration_ms:
+ roll_started_at && roll_stopped_at
+ ? roll_stopped_at - roll_started_at
+ : null,
+ rt: performance.now() - start_time,
+ };
+ display_element.innerHTML = '';
+ this.jsPsych.finishTrial(trial_data);
+ };
+
+ button_element.addEventListener('click', () => {
+ if (!button_element.disabled) {
+ end_trial();
+ }
+ });
+ }
+
+ build_markup(prompt, button_label) {
+ const prompt_html = prompt
+ ? `${prompt}
`
+ : '';
+ return `
+
+ ${prompt_html}
+
?
+
+
${button_label}
+
+ `;
+ }
+
+ random_face(exclude_value) {
+ let value = Math.floor(Math.random() * 6) + 1;
+ if (value === exclude_value) {
+ value = (value % 6) + 1;
+ }
+ return value;
+ }
+
+ get_rigged_value(value) {
+ const numeric = Number(value);
+ if (Number.isFinite(numeric) && numeric >= 1 && numeric <= 6) {
+ return Math.round(numeric);
+ }
+ return null;
+ }
+}
+
+DieRollPlugin.info = info;
+
+export default DieRollPlugin;
diff --git a/scripts/text-stimuli.js b/scripts/text-stimuli.js
index f8ef0e4..22f3b26 100644
--- a/scripts/text-stimuli.js
+++ b/scripts/text-stimuli.js
@@ -9,26 +9,30 @@ export const textStimuli = {
};
function getStimulusMap() {
-
const stimulusMap = new Map();
-
- stimulusMap.set('pre_survey_info', html`
-
- 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.
-
-
- Nonsense or random answers may lead to your submission being rejected.
-
-
- Press
- SPACE
- to continue.
-
- `
+ stimulusMap.set(
+ 'pre_survey_info',
+ html`
+
+ 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.
+
+
+ Nonsense or random answers may lead to your submission being rejected.
+
+
+ Press
+ SPACE
+ to continue.
+
+ `
);
-
stimulusMap.set(
'pre_consent_info',
html`
@@ -105,15 +109,16 @@ function getStimulusMap() {
The controller within the meaning of the EU General Data Protection
Regulation (GDPR) and other national data protection laws of the member
- states, as well as other data protection regulations is the University of Muenster, represented by the Rector, Prof. Dr.
- Johannes Wessels, Schlossplatz 2, 48149 MünsterTel.: + 49 251
- 83-0E-Mail: verwaltung@uni-muenster.de
+ states, as well as other data protection regulations is the University
+ of Muenster, represented by the Rector, Prof. Dr. Johannes Wessels,
+ Schlossplatz 2, 48149 MünsterTel.: + 49 251 83-0E-Mail:
+ verwaltung@uni-muenster.de
8. Contact details of the data protection officer
- The data protection officer of the University of Muenster is: Nina Meyer-Pachur
- Schlossplatz 2, 48149 Münster Tel.: + 49 251 83-22446 E-Mail:
- datenschutz@uni-muenster.de
+ The data protection officer of the University of Muenster is: Nina
+ Meyer-Pachur Schlossplatz 2, 48149 Münster Tel.: + 49 251 83-22446
+ E-Mail: datenschutz@uni-muenster.de
9. Reference to the rights of those affected
@@ -159,14 +164,68 @@ function getStimulusMap() {
`
);
-
stimulusMap.set(
'instructions_1',
html`
- TEST
+ In this experiment, you will be solving some captchas.
+
+ This will involve identifying slightly distorted letters and numbers
+ from an image.
+
+
+ Your task is to solve the captchas as quickly and accurately as
+ possible. You will not be given feedback on your performance.
+
+
+ Press
+ SPACE
+ to continue.
+
`
);
+ stimulusMap.set(
+ 'instructions_2',
+ html`
+
+ To randomise the difficulty of the captchas you will be solving, we will
+ first ask you to roll a virtual die. You will then be shown captchas of
+ a difficulty corresponding to the number you rolled.
+
+
+ The higher the number you roll, the more difficult the captchas will be.
+
+
+ Press
+ SPACE
+ to continue.
+
+ `
+ );
+
+ stimulusMap.set(
+ 'instructions_3',
+ html`
+
+ You will now proceed to the captcha task, in which you will need to
+ solve 12 captchas in a row as quickly and accurately as possible.
+
+
+ Please ensure that you are in a quiet environment and that you will not
+ be interrupted for the next few minutes.
+
+
+ Please also ensure that you solve these captchas to the best of your
+ ability. Nonsense or random answers may lead to your submission being
+ rejected.
+
+
+ Press
+ SPACE
+ when you are ready to begin.
+
+ `
+ );
stimulusMap.set(
'debrief',
@@ -184,10 +243,13 @@ function getStimulusMap() {
To this end, we created the appearance of additional participants in
- the experiment – in reality, you performed the task alone with your 'partner's' actions being peformed by a computer.
+ the experiment – in reality, you performed the task alone with your
+ 'partner's' actions being peformed by a computer.
- You may have also read in the post-experiment questionnaire that your partner was either an AI agent – this was to examine whether people’s suspicions are biased by the way in which they are asked about them.
+ You may have also read in the post-experiment questionnaire that your
+ partner was either an AI agent – this was to examine whether people’s
+ suspicions are biased by the way in which they are asked about them.
Should you have any additional questions, you may contact Dr Shaheed
diff --git a/styles.css b/styles.css
index 1554e7f..bbc53c7 100644
--- a/styles.css
+++ b/styles.css
@@ -29,4 +29,129 @@
.object-moving-box {
width: 3vw;
height: 3vw;
-}
\ No newline at end of file
+}
+
+.jspsych_die_roll {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ gap: 1.25rem;
+}
+
+.jspsych_die_roll_prompt {
+ max-width: 40rem;
+}
+
+.jspsych_die_roll_face {
+ width: 7.5rem;
+ height: 7.5rem;
+ border-radius: 1rem;
+ border: 4px solid #0f172a;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 3.5rem;
+ font-weight: 700;
+ background: linear-gradient(145deg, #f1f5f9, #e2e8f0);
+ box-shadow: 0 20px 35px rgba(15, 23, 42, 0.2);
+ cursor: pointer;
+ user-select: none;
+ transition: transform 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
+}
+
+.jspsych_die_roll_result {
+ font-weight: 600;
+ min-height: 1.5rem;
+}
+
+.jspsych_die_roll_face--interactive:hover {
+ transform: scale(1.03);
+ box-shadow: 0 24px 40px rgba(15, 23, 42, 0.25);
+}
+
+.jspsych_die_roll_face--active {
+ animation: pulse 0.6s ease-in-out infinite alternate;
+}
+
+.jspsych_die_roll_face--locked {
+ cursor: not-allowed;
+ opacity: 0.8;
+ box-shadow: 0 15px 25px rgba(15, 23, 42, 0.15);
+}
+
+@keyframes pulse {
+ from {
+ transform: scale(1);
+ }
+ to {
+ transform: scale(1.05);
+ }
+}
+
+.jspsych_captcha {
+ max-width: 34rem;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ align-items: center;
+ text-align: center;
+}
+
+.jspsych_captcha_canvas_wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.75rem;
+ width: 100%;
+}
+
+.jspsych_captcha_canvas {
+ border-radius: 0.75rem;
+ border: 2px solid #cbd5f5;
+ background: #fff;
+}
+
+.jspsych_captcha_refresh {
+ border: none;
+ background: #e2e8f0;
+ border-radius: 9999px;
+ width: 2.75rem;
+ height: 2.75rem;
+ font-size: 1.35rem;
+ cursor: pointer;
+}
+
+.jspsych_captcha_label {
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+ font-weight: 600;
+ align-items: center;
+ width: 100%;
+}
+
+.jspsych_captcha_input {
+ border: 2px solid #cbd5f5;
+ border-radius: 0.5rem;
+ padding: 0.75rem 1rem;
+ font-size: 1.25rem;
+ letter-spacing: 0.2rem;
+ text-transform: uppercase;
+ text-align: center;
+ max-width: 16rem;
+}
+
+.jspsych_captcha_length_hint {
+ font-size: 0.9rem;
+ font-weight: 500;
+ color: #475569;
+ min-height: 1.25rem;
+}
+
+.jspsych_captcha_error {
+ min-height: 1.25rem;
+ color: #b91c1c;
+ font-weight: 600;
+}