diff --git a/README.md b/README.md
index 0ddf4da..0878419 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,12 @@ To deliver this task offline, open `index.html` in a browser of choice. To deliv
To deliver this task offline in a spectrometer, adjust the key layout defined in `index.ts` to map the signals generated by spectrometer controllers. Run `yarn build:spectrometer` to generate output with updated button icons and key layouts to suit spectrometer delivery and controller interaction. This task requires a minimum of four physical buttons for playing the game and a signal signifying spectrometer data collection.
+> [!IMPORTANT]
+> When using the Current Designs button box, set the following *Output modes* for the corresponding task version:
+>
+> * **v1.8.2 and higher**: Set *Output mode* to `HID_NAR_12345`
+> * **v1.8.1 and earlier**: Set *Output mode* to `HID_KEY_12345`
+
## License
diff --git a/analysis/rdk_data_dictionary_v1.7.1.md b/analysis/v1.7.1_data_dictionary.md
similarity index 100%
rename from analysis/rdk_data_dictionary_v1.7.1.md
rename to analysis/v1.7.1_data_dictionary.md
diff --git a/analysis/v1.8.2_data_dictionary.md b/analysis/v1.8.2_data_dictionary.md
new file mode 100644
index 0000000..9c9109d
--- /dev/null
+++ b/analysis/v1.8.2_data_dictionary.md
@@ -0,0 +1,47 @@
+# RDK Task - Data Dictionary
+
+> Version 1.8.2
+
+Release generated October 16, 2025.
+
+Browse files: [https://github.com/Brain-Development-and-Disorders-Lab/task_rdk/tree/v1.8.2](https://github.com/Brain-Development-and-Disorders-Lab/task_rdk/tree/v1.8.2)
+
+## Data
+
+### General
+
+- `trialNumber`: Number of elapsed trials, indexed from `0`.
+- `score`: Total number of correct responses, counting `dot-game`-type `main` trials only (not calibration or practice trials).
+- `deviation`: The direction of the coherent dots, either `left` or `right`.
+- `correct`: Whether the participant selected the correct direction of the coherent dots, either `1` or `0`.
+- `coherence`: The current proportion of coherent dots being presented during the moving dots stimulus. Example: A value of `0.13` indicates 13% of dots on-screen will be coherent, the remaining 87% will be random distractor dots.
+- `coherences`: Generated low and high coherence pairs, `[low, high]`. Calculated as `[kMedian * 0.5, kMedian * 2.0]`, where `kMedian <= 0.5 && kMedian >= 0.12`.
+
+### Decisions
+
+- `selection`: The selected button value, `vc_l`, `sc_l`, `sc_r`, or `vc_r`.
+
+### Timing
+
+- `trialStartTime`: Timestamp of when the start of the trial is invoked.
+- `trialEndTime`: Timestamp of when the trial is ended and the next trial invoked.
+- `trialTotalTime`: Total duration of the trial presentation.
+- `referenceStartTime`: Timestamp of when the start of the dot direction decision is presented.
+- `referenceEndTime`: Timestamp of when the direction is selected and the selection is stored.
+- `referenceTotalTime`: Total duration of the direction decision being presented and the participant making a selection.
+- `confidenceStartTime`: Timestamp of when the start of the confidence slider is presented.
+- `confidenceEndTime`: Timestamp of when the confidence value has been selected and the trial continues.
+- `confidenceTotalTime`: Total duration of the confidence slider being presented and the participant making a slider selection.
+- `stimulusDuration`: Total duration that the moving dot stimulus is presented for (default is `1500` milliseconds).
+
+## Trial Structure
+
+1. `initial`: Presenting a fixation cross for `1000` milliseconds.
+2. `motion`: Presenting the dot motion for `trial.stimulusDuration` milliseconds (default `1500`).
+3. `reference`: Presenting the dot direction decision, no time limit.
+4. `decision`: Presenting a fixation cross for `250` milliseconds.
+5. `confidence`: Presenting the confidence selection slider **if enabled on that trial**, no time limit.
+
+## Notes
+
+- Due to inaccuracies when using millisecond-precision timing alongside complex 2D rendering on the web, the result for `trialTotalTime` is typically between `65`-`75` milliseconds **greater** than the sum of other `-TotalTime` values and fixed durations.
diff --git a/package.json b/package.json
index ea6e798..dd4b6ed 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "task_rdk",
- "version": "1.8.1",
+ "version": "1.8.2",
"description": "RDK Task",
"scripts": {
"build": "yarn build:desktop",
diff --git a/src/classes/Graphics.ts b/src/classes/Graphics.ts
index 2f3b829..56106db 100644
--- a/src/classes/Graphics.ts
+++ b/src/classes/Graphics.ts
@@ -103,7 +103,7 @@ export class Graphics {
);
if (
this.trial.showFeedback === true &&
- this.trial.data.referenceSelection !== ""
+ this.trial.data.selection !== ""
) {
if (this.trial.data.correct === 1) {
this.renderer.createFixation(0, 0, fixationDiameter, false, Renderer.getInvertedColor("green"));
@@ -150,7 +150,7 @@ export class Graphics {
const startAngle = reference - Math.PI / 4;
const endAngle = startAngle + Math.PI / 4;
this.renderer.createArc(startAngle - Math.PI / 128, endAngle, Renderer.getInvertedColor("white"));
- this.renderer.createArc(startAngle, endAngle, Renderer.getInvertedColor("#3ea3a3"));
+ this.renderer.createArc(startAngle, endAngle, Renderer.getInvertedColor("#1792fc"));
}
/**
@@ -159,7 +159,7 @@ export class Graphics {
addRightArc(): void {
const startAngle = -Math.PI / 2;
const endAngle = Math.PI / 2;
- this.renderer.createArc(startAngle, endAngle, Renderer.getInvertedColor("#3ea3a3"));
+ this.renderer.createArc(startAngle, endAngle, Renderer.getInvertedColor("#1792fc"));
}
/**
@@ -256,113 +256,6 @@ export class Graphics {
}
}
- /**
- * Setup HTML elements for confidence selection element
- * @param parameters configuration information for confidence selection
- */
- addConfidence(parameters: any): void {
- // Confidence instructions
- let html = "";
-
- // Controls for confidence slider and submission
- if (__TARGET__ === "spectrometer") {
- html += `
`;
- // Decrease confidence
- html += `
`;
- html += Renderer.getEmbeddedControllerButton(1);
- html += `
Decrease Confidence
`;
- html += `
`;
-
- // Submit
- html += `
`;
- html += `
Continue
`;
- html += Renderer.getEmbeddedControllerButton(3);
- html += `
`;
-
- // Increase confidence
- html += `
`;
- html += `
Increase Confidence
`;
- html += Renderer.getEmbeddedControllerButton(4);
- html += `
`;
- html += `
`;
- } else {
- html += ``;
- // Decrease confidence
- html += `
`;
- html += Renderer.getEmbeddedKeyboardButton("F");
- html += `
Decrease Confidence
`;
- html += `
`;
-
- // Submit
- html += `
`;
- html += `
Continue
`;
- html += Renderer.getEmbeddedKeyboardButton("K");
- html += `
`;
-
- // Increase confidence
- html += `
`;
- html += `
Increase Confidence
`;
- html += Renderer.getEmbeddedKeyboardButton("J");
- html += `
`;
- html += `
`;
- }
-
- // Confidence slider
- html += ``;
- html += `
`;
- html += `
`;
- html += ``;
-
- // Add labels
- const labels = ["50%", "60%", "70%", "80%", "90%", "100%"];
- html += `
`;
- for (let i = 0; i < labels.length; i++) {
- const width = 100 / (labels.length - 1);
- const offset = i * width - width / 2;
- html += `
`;
- html += `${labels[i]} `;
- html += `
`;
- }
- html += `
`;
- html += `
`;
- html += `
`;
- html += `
`;
-
- // Button to notify of a mistake
- html += ``;
-
- // Add the HTML to the display element
- this.renderer.getDisplayElement().parentNode.innerHTML = html;
-
- // Try to hide the thumb
- document
- .getElementById("confidence-slider")
- .addEventListener("click", () => {
- const slider = document.getElementById("confidence-slider");
- slider.className = "confidence-slider";
- });
-
- // Bind appropriate event listeners to actions
- document.addEventListener("keyup", parameters.eventHandler);
- if (__TARGET__ === "desktop") {
- document
- .getElementById("mistake-button")
- .addEventListener("click", parameters.eventHandler);
- }
- }
-
/**
* Clear all elements from the renderer
* This removes everything including aperture outline and fixation cross
@@ -386,5 +279,8 @@ export class Graphics {
for (let i = 0; i < trackedElements.length; i++) {
this.renderer.getTarget().remove(trackedElements[i]);
}
+
+ // Clear decision containers if they exist
+ this.renderer.clearDecisionContainers();
}
}
diff --git a/src/classes/Renderer.ts b/src/classes/Renderer.ts
index 6fbf60e..f76c479 100644
--- a/src/classes/Renderer.ts
+++ b/src/classes/Renderer.ts
@@ -256,29 +256,91 @@ export class Renderer {
}
}
- addButton(buttonText: string, offset = "none"): HTMLDivElement {
+ addButton(buttonText: string, variant = "default"): HTMLDivElement {
+ // Create colorscheme based on variant
+ const colorscheme = {
+ default: {
+ background: "#88919e",
+ fill: "#575d66",
+ },
+ orangeDark: {
+ background: "#ffc20a",
+ fill: "#ffdb6e",
+ },
+ orangeLight: {
+ background: "#ffd75e",
+ fill: "#fce7a9",
+ },
+ blueDark: {
+ background: "#1792fc",
+ fill: "#47a8fc",
+ },
+ blueLight: {
+ background: "#7bbef8",
+ fill: "#9dd0fc",
+ },
+ };
+
// Create container
const buttonContainer = document.createElement("div");
buttonContainer.style.display = "flex";
buttonContainer.style.justifyContent = "center";
buttonContainer.style.alignItems = "center";
- buttonContainer.style.width = "100px";
- buttonContainer.style.height = "50px";
- buttonContainer.style.padding = "2px";
- buttonContainer.style.border = "4px solid " + Renderer.getInvertedColor("black");
- buttonContainer.style.borderRadius = "12px";
- buttonContainer.style.backgroundColor = Renderer.getInvertedColor("white");
+ buttonContainer.style.width = "140px";
+ buttonContainer.style.height = "60px";
+ buttonContainer.style.padding = "4px";
+ buttonContainer.style.borderRadius = "8px";
+ buttonContainer.style.backgroundColor = colorscheme[variant].background;
+ buttonContainer.style.position = "relative";
+ buttonContainer.style.overflow = "hidden";
+
+ // Create progress bar
+ const progressBar = document.createElement("div");
+ progressBar.style.position = "absolute";
+ progressBar.style.bottom = "0";
+ progressBar.style.left = "0";
+ progressBar.style.width = "0%";
+ progressBar.style.height = "100%";
+ progressBar.style.backgroundColor = colorscheme[variant].fill;
+ progressBar.style.transition = "width 2s linear";
+ progressBar.className = "progress-bar";
+ buttonContainer.appendChild(progressBar);
// Add label text for the button
const buttonLabel = document.createElement("p");
buttonLabel.textContent = buttonText;
buttonLabel.style.fontWeight = "bold";
- buttonLabel.style.fontSize = "xx-large";
+ buttonLabel.style.fontSize = "x-large";
+ buttonLabel.style.position = "relative";
+ buttonLabel.style.zIndex = "1";
+ buttonLabel.style.color = "black";
buttonContainer.appendChild(buttonLabel);
return buttonContainer;
}
+ /**
+ * Start progress animation for a button
+ * @param {HTMLDivElement} buttonElement the button element to animate
+ */
+ startProgress(buttonElement: HTMLDivElement): void {
+ const progressBar = buttonElement.querySelector('.progress-bar') as HTMLElement;
+ if (progressBar) {
+ progressBar.style.width = "100%";
+ }
+ }
+
+ /**
+ * Stop progress animation for a button
+ * @param {HTMLDivElement} buttonElement the button element to reset
+ */
+ stopProgress(buttonElement: HTMLDivElement): void {
+ const progressBar = buttonElement.querySelector('.progress-bar') as HTMLElement;
+ if (progressBar) {
+ progressBar.style.width = "0%";
+ }
+ }
+
/**
* Add a label onto the target
* @param {string} labelType the type of label to add
@@ -289,29 +351,69 @@ export class Renderer {
const graphicsCanvasDiv =
document.getElementsByClassName("graphics-container")[0];
- // Create the left label container
+ // Create the left container
const leftContainer = document.createElement("div");
+ leftContainer.id = "left-container";
leftContainer.style.display = "flex";
- leftContainer.style.justifyContent = "center";
- leftContainer.style.alignItems = "center";
leftContainer.style.flexDirection = "column";
- leftContainer.style.gap = "10px";
+ leftContainer.style.alignItems = "center";
+ leftContainer.style.justifyContent = "center";
leftContainer.style.position = "absolute";
- leftContainer.style.marginRight = "60%";
- leftContainer.style.marginTop = "40px";
+ leftContainer.style.marginRight = "70%";
- // Add the left button to the left label container
- const leftButton = this.addButton("Left", "left");
- leftContainer.append(leftButton);
+ // Left label
+ const leftLabel = document.createElement("p");
+ leftLabel.textContent = "Left";
+ leftLabel.style.fontSize = "xx-large";
+ leftLabel.style.fontWeight = "bold";
+ leftContainer.append(leftLabel);
- // Add the input indicators
+ // Create the left button container
const leftButtonContainer = document.createElement("div");
+ leftButtonContainer.style.display = "flex";
+ leftButtonContainer.style.justifyContent = "center";
+ leftButtonContainer.style.alignItems = "center";
+ leftButtonContainer.style.flexDirection = "row";
+ leftButtonContainer.style.gap = "16px";
+ leftContainer.append(leftButtonContainer);
+
+ // Left "Very Confident" button
+ const leftVeryConfidentButtonContainer = document.createElement("div");
+ leftVeryConfidentButtonContainer.style.display = "flex";
+ leftVeryConfidentButtonContainer.style.justifyContent = "center";
+ leftVeryConfidentButtonContainer.style.alignItems = "center";
+ leftVeryConfidentButtonContainer.style.flexDirection = "column";
+ leftVeryConfidentButtonContainer.style.gap = "16px";
+ const leftVeryConfidentButton = this.addButton("Very Confident", "orangeDark");
+ leftVeryConfidentButton.setAttribute("data-button-id", "vc_l");
+ leftVeryConfidentButtonContainer.append(leftVeryConfidentButton);
+ const leftVeryConfidentButtonLabelContainer = document.createElement("div");
if (__TARGET__ === "spectrometer") {
- leftButtonContainer.innerHTML = Renderer.getEmbeddedControllerButton(1);
+ leftVeryConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedControllerButton(1);
} else {
- leftButtonContainer.innerHTML = Renderer.getEmbeddedKeyboardButton("F");
+ leftVeryConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedKeyboardButton("D");
}
- leftContainer.append(leftButtonContainer);
+ leftVeryConfidentButtonContainer.append(leftVeryConfidentButtonLabelContainer);
+ leftButtonContainer.append(leftVeryConfidentButtonContainer);
+
+ // Left "Somewhat Confident" button
+ const leftSomewhatConfidentButtonContainer = document.createElement("div");
+ leftSomewhatConfidentButtonContainer.style.display = "flex";
+ leftSomewhatConfidentButtonContainer.style.justifyContent = "center";
+ leftSomewhatConfidentButtonContainer.style.alignItems = "center";
+ leftSomewhatConfidentButtonContainer.style.flexDirection = "column";
+ leftSomewhatConfidentButtonContainer.style.gap = "16px";
+ const leftSomewhatConfidentButton = this.addButton("Somewhat Confident", "orangeLight");
+ leftSomewhatConfidentButton.setAttribute("data-button-id", "sc_l");
+ leftSomewhatConfidentButtonContainer.append(leftSomewhatConfidentButton);
+ const leftSomewhatConfidentButtonLabelContainer = document.createElement("div");
+ if (__TARGET__ === "spectrometer") {
+ leftSomewhatConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedControllerButton(2);
+ } else {
+ leftSomewhatConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedKeyboardButton("F");
+ }
+ leftSomewhatConfidentButtonContainer.append(leftSomewhatConfidentButtonLabelContainer);
+ leftButtonContainer.append(leftSomewhatConfidentButtonContainer);
// Prepend the left label container to the graphics container
graphicsCanvasDiv.prepend(leftContainer);
@@ -320,29 +422,69 @@ export class Renderer {
const graphicsCanvasDiv =
document.getElementsByClassName("graphics-container")[0];
- // Create the right label container
+ // Create the right container
const rightContainer = document.createElement("div");
+ rightContainer.id = "right-container";
rightContainer.style.display = "flex";
- rightContainer.style.justifyContent = "center";
- rightContainer.style.alignItems = "center";
rightContainer.style.flexDirection = "column";
- rightContainer.style.gap = "10px";
+ rightContainer.style.alignItems = "center";
+ rightContainer.style.justifyContent = "center";
rightContainer.style.position = "absolute";
- rightContainer.style.marginLeft = "60%";
- rightContainer.style.marginTop = "40px";
+ rightContainer.style.marginLeft = "70%";
- // Add the right button to the right label container
- const rightButton = this.addButton("Right", "right");
- rightContainer.append(rightButton);
+ // Right label
+ const rightLabel = document.createElement("p");
+ rightLabel.textContent = "Right";
+ rightLabel.style.fontSize = "xx-large";
+ rightLabel.style.fontWeight = "bold";
+ rightContainer.append(rightLabel);
- // Add the input indicators
+ // Create the right button container
const rightButtonContainer = document.createElement("div");
+ rightButtonContainer.style.display = "flex";
+ rightButtonContainer.style.justifyContent = "center";
+ rightButtonContainer.style.alignItems = "center";
+ rightButtonContainer.style.flexDirection = "row";
+ rightButtonContainer.style.gap = "16px";
+ rightContainer.append(rightButtonContainer);
+
+ // Right "Somewhat Confident" button
+ const rightSomewhatConfidentButtonContainer = document.createElement("div");
+ rightSomewhatConfidentButtonContainer.style.display = "flex";
+ rightSomewhatConfidentButtonContainer.style.justifyContent = "center";
+ rightSomewhatConfidentButtonContainer.style.alignItems = "center";
+ rightSomewhatConfidentButtonContainer.style.flexDirection = "column";
+ rightSomewhatConfidentButtonContainer.style.gap = "16px";
+ const rightSomewhatConfidentButton = this.addButton("Somewhat Confident", "blueLight");
+ rightSomewhatConfidentButton.setAttribute("data-button-id", "sc_r");
+ rightSomewhatConfidentButtonContainer.append(rightSomewhatConfidentButton);
+ const rightSomewhatConfidentButtonLabelContainer = document.createElement("div");
if (__TARGET__ === "spectrometer") {
- rightButtonContainer.innerHTML = Renderer.getEmbeddedControllerButton(4);
+ rightSomewhatConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedControllerButton(3);
} else {
- rightButtonContainer.innerHTML = Renderer.getEmbeddedKeyboardButton("J");
+ rightSomewhatConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedKeyboardButton("J");
}
- rightContainer.append(rightButtonContainer);
+ rightSomewhatConfidentButtonContainer.append(rightSomewhatConfidentButtonLabelContainer);
+ rightButtonContainer.append(rightSomewhatConfidentButtonContainer);
+
+ // Right "Very Confident" button
+ const rightVeryConfidentButtonContainer = document.createElement("div");
+ rightVeryConfidentButtonContainer.style.display = "flex";
+ rightVeryConfidentButtonContainer.style.justifyContent = "center";
+ rightVeryConfidentButtonContainer.style.alignItems = "center";
+ rightVeryConfidentButtonContainer.style.flexDirection = "column";
+ rightVeryConfidentButtonContainer.style.gap = "16px";
+ const rightVeryConfidentButton = this.addButton("Very Confident", "blueDark");
+ rightVeryConfidentButton.setAttribute("data-button-id", "vc_r");
+ rightVeryConfidentButtonContainer.append(rightVeryConfidentButton);
+ const rightVeryConfidentButtonLabelContainer = document.createElement("div");
+ if (__TARGET__ === "spectrometer") {
+ rightVeryConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedControllerButton(4);
+ } else {
+ rightVeryConfidentButtonLabelContainer.innerHTML = Renderer.getEmbeddedKeyboardButton("K");
+ }
+ rightVeryConfidentButtonContainer.append(rightVeryConfidentButtonLabelContainer);
+ rightButtonContainer.append(rightVeryConfidentButtonContainer);
// Append the right label container to the graphics container
graphicsCanvasDiv.append(rightContainer);
@@ -364,13 +506,13 @@ export class Renderer {
// Create SVG with 4 circles representing the controller buttons
const svg = `
-
-
+
${[1, 2, 3, 4].map((index) => {
- const centerX = 16 * index;
- const centerY = 20;
+ const centerX = 18 * index;
+ const centerY = 22.5;
const radius = 6;
const isHighlighted = index === validIndex;
@@ -477,7 +619,7 @@ export class Renderer {
/**
* Clear all HTML elements from the display
- * This removes HTML elements like confidence sliders, labels, etc.
+ * This removes HTML elements like labels, etc.
*/
clearHTMLElements(): void {
const graphicsContainer = document.getElementsByClassName("graphics-container")[0];
@@ -494,6 +636,22 @@ export class Renderer {
}
}
+ /**
+ * Clear the left and right decision containers
+ * This removes only the decision UI elements without affecting other HTML elements
+ */
+ clearDecisionContainers(): void {
+ const leftContainer = document.getElementById("left-container");
+ if (leftContainer) {
+ leftContainer.remove();
+ }
+
+ const rightContainer = document.getElementById("right-container");
+ if (rightContainer) {
+ rightContainer.remove();
+ }
+ }
+
/**
* Get the width of the view
* @return {number} the width of the view
diff --git a/src/classes/Stimulus.ts b/src/classes/Stimulus.ts
index c4f2f76..e8017d0 100644
--- a/src/classes/Stimulus.ts
+++ b/src/classes/Stimulus.ts
@@ -56,12 +56,21 @@ export class Stimulus {
createKeybindings(): void {
// Check if the stimulus can be interacted with
if (this.interactive) {
- // Bind keys
+ // Bind keydown events
for (const binding in this.keybindings) {
if (this.keybindings[binding]) {
- document.addEventListener("keyup", this.keybindings[binding].handler);
+ document.addEventListener("keydown", this.keybindings[binding].handler);
}
}
+
+ // Also bind keyup events for decision stimulus
+ if (this.name === "decision") {
+ document.addEventListener("keyup", (event: KeyboardEvent) => {
+ if (window.decisionKeyUpHandler) {
+ window.decisionKeyUpHandler(event);
+ }
+ });
+ }
}
}
@@ -69,15 +78,26 @@ export class Stimulus {
* Remove keybindings for the stimulus
*/
removeKeybindings(): void {
- // Unbind keys
+ // Unbind keydown events
for (const binding in this.keybindings) {
if (this.keybindings[binding]) {
document.removeEventListener(
- "keyup",
+ "keydown",
this.keybindings[binding].handler
);
}
}
+
+ // Also remove keyup events for decision stimulus
+ if (this.name === "decision") {
+ document.removeEventListener("keyup", window.decisionKeyUpHandler);
+ }
+
+ // Remove post-decision keyup handler if it exists
+ if (this.name === "post-decision" && window.postDecisionKeyUpHandler) {
+ document.removeEventListener("keyup", window.postDecisionKeyUpHandler);
+ window.postDecisionKeyUpHandler = undefined;
+ }
}
/**
@@ -150,8 +170,6 @@ export class Stimulus {
graphics.addRightArc();
} else if (component === "indicator") {
graphics.addReferenceIndicator();
- } else if (component === "confidence") {
- graphics.addConfidence(parameters);
} else if (component === "left") {
graphics.addLeftLabel();
} else if (component === "right") {
diff --git a/src/css/styles.css b/src/css/styles.css
index db470da..e44af3a 100644
--- a/src/css/styles.css
+++ b/src/css/styles.css
@@ -1,7 +1,5 @@
/*
-Stylesheet for Bang et al. RDK task. Primary
-style features relate to slider input used for
-confidence estimate.
+Stylesheet for Bang et al. RDK task.
Author: Henry Burgess
*/
@@ -34,69 +32,6 @@ Author: Henry Burgess
margin: 5%;
}
-.confidence-slider {
- -webkit-appearance: none;
- margin-top: 10px;
- width: 100%;
- height: 15px;
- border-radius: 5px;
- background: #000000;
- outline: none;
- opacity: 0.7;
- -webkit-transition: 0.2s;
- transition: opacity 0.2s;
-}
-
-.confidence-slider::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- width: 1.5rem;
- height: 64px;
- border-radius: 10px;
- background: #ff70ff;
- cursor: pointer;
-}
-
-.confidence-slider::-moz-range-thumb {
- width: 1.5rem;
- height: 64px;
- border-radius: 10px;
- background: ff70ff;
- cursor: pointer;
-}
-
-.confidence-slider-hidden {
- -webkit-appearance: none;
- width: 100%;
- height: 15px;
- border-radius: 5px;
- background: #000000;
- outline: none;
- opacity: 0.7;
- -webkit-transition: 0.2s;
- transition: opacity 0.2s;
-}
-
-.confidence-slider-hidden::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- width: 1.5rem;
- height: 64px;
- border-radius: 10px;
- background: ff70ff;
- cursor: pointer;
- visibility: hidden;
-}
-
-.confidence-slider-hidden::-moz-range-thumb {
- width: 1.5rem;
- height: 64px;
- border-radius: 10px;
- background: ff70ff;
- cursor: pointer;
- visibility: hidden;
-}
-
.reference-instructions-container {
padding: 0;
}
@@ -112,38 +47,6 @@ Author: Henry Burgess
text-align: center;
}
-#confidence-controls-container {
- display: flex;
- flex-direction: row;
- width: 90vw;
- justify-content: space-between;
- align-items: center;
-}
-
-#confidence-controls-container-element {
- display: flex;
- flex-direction: row;
- gap: 10px;
- justify-content: center;
- align-items: center;
- width: 100%;
-}
-
-#mistake-button-container {
- display: flex;
- flex-direction: row;
- gap: 10px;
- margin-top: 24px;
- justify-content: center;
- align-items: center;
- width: 100%;
-}
-
-#mistake-button-container button {
- font-size: 18px;
- font-weight: bold;
-}
-
/* Base control button class */
.control-button {
font-family: Arial, Helvetica, sans-serif;
@@ -200,14 +103,6 @@ body.inverted {
color: white;
}
-.graphics-container.inverted .confidence-slider {
- background: #ffffff;
-}
-
-.graphics-container.inverted .confidence-slider-hidden {
- background: #ffffff;
-}
-
/* Invert label borders */
body.inverted .addButton {
border-color: white !important;
diff --git a/src/index.ts b/src/index.ts
index 875e6e9..2308007 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -66,18 +66,18 @@ const jsPsych = initJsPsych({
const InputConfigurations = {
desktop: {
name: "desktop",
- left: "f",
- right: "j",
- submit: "k",
- alt: "d",
+ 1: "d",
+ 2: "f",
+ 3: "j",
+ 4: "k",
showButtons: false,
},
spectrometer: {
name: "spectrometer",
- left: "4",
- right: "1",
- submit: "2",
- alt: "3",
+ 1: "1",
+ 2: "2",
+ 3: "3",
+ 4: "4",
trigger: "5",
showButtons: false,
},
@@ -87,21 +87,21 @@ const InputConfigurations = {
export const Manipulations = {
numTutorialTrials: jsPsych.extensions.Neurocog.getManipulation(
"numTutorialTrials",
- 10
+ 8
),
numPracticeTrials: jsPsych.extensions.Neurocog.getManipulation(
"numPracticeTrials",
- 10
+ 8
),
numCalibrationOneTrials: jsPsych.extensions.Neurocog.getManipulation(
"numCalibrationOneTrials",
- 120
+ 36
),
numMainTrials: jsPsych.extensions.Neurocog.getManipulation(
"numMainTrials",
- 200
+ 40
),
- invertColors: jsPsych.extensions.Neurocog.getManipulation("invertColors", false),
+ invertColors: __TARGET__ === "spectrometer",
requireID: jsPsych.extensions.Neurocog.getManipulation("requireID", false),
enableFullscreen: jsPsych.extensions.Neurocog.getManipulation("enableFullscreen", false),
showInstructions: jsPsych.extensions.Neurocog.getManipulation(
@@ -177,8 +177,8 @@ if (
instructionContinueText,
],
allow_keys: !keyLayout.showButtons,
- key_forward: keyLayout.right.charAt(keyLayout.right.length - 1),
- key_backward: keyLayout.left.charAt(keyLayout.left.length - 1),
+ key_forward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["4"] : keyLayout["3"],
+ key_backward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["1"] : keyLayout["2"],
show_page_number: true,
show_clickable_nav: keyLayout.showButtons,
});
@@ -198,8 +198,8 @@ timeline.push({
type: InstructionsPlugin,
pages: tutorialGames,
allow_keys: !keyLayout.showButtons,
- key_forward: keyLayout.right.charAt(keyLayout.right.length - 1),
- key_backward: keyLayout.left.charAt(keyLayout.left.length - 1),
+ key_forward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["4"] : keyLayout["3"],
+ key_backward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["1"] : keyLayout["2"],
show_page_number: true,
show_clickable_nav: keyLayout.showButtons,
});
@@ -226,7 +226,6 @@ for (let t = 0; t < Manipulations.numTutorialTrials; t++) {
dotDirection: r,
dotVelocity: 2.0,
showFeedback: false,
- checkConfidence: false,
keyLayout: keyLayout,
data: {
name: trialName,
@@ -234,10 +233,9 @@ for (let t = 0; t < Manipulations.numTutorialTrials; t++) {
coherence: k,
stimulusDuration: d,
dotDirection: r,
- referenceSelection: "",
+ selection: "",
deviation: "left",
correct: false,
- confidenceSelection: 0,
},
extensions: [{ type: NeurocogExtension }],
};
@@ -251,8 +249,7 @@ const practice = [
`Practice Games ` +
`You will now play another ${Manipulations.numPracticeTrials} ` +
`practice games. ` +
- `You won't have to rate your confidence after each game, ` +
- `but you will be shown if your answer was correct or not.
` +
+ `You will be shown if your answer was correct or not.` +
`If your answer was correct, the cross in the ` +
`middle of the screen will briefly turn green.
` +
`If your answer was wrong, the cross in the ` +
@@ -264,8 +261,8 @@ timeline.push({
type: InstructionsPlugin,
pages: practice,
allow_keys: !keyLayout.showButtons,
- key_forward: keyLayout.right.charAt(keyLayout.right.length - 1),
- key_backward: keyLayout.left.charAt(keyLayout.left.length - 1),
+ key_forward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["4"] : keyLayout["3"],
+ key_backward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["1"] : keyLayout["2"],
show_page_number: true,
show_clickable_nav: keyLayout.showButtons,
});
@@ -289,7 +286,6 @@ for (let t = 0; t < Manipulations.numPracticeTrials; t++) {
dotDirection: r,
dotVelocity: 2.0,
showFeedback: true,
- checkConfidence: false,
keyLayout: keyLayout,
data: {
name: trialName,
@@ -297,10 +293,9 @@ for (let t = 0; t < Manipulations.numPracticeTrials; t++) {
coherence: k,
stimulusDuration: 1500,
dotDirection: r,
- referenceSelection: "",
+ selection: "",
deviation: deviation,
correct: false,
- confidenceSelection: 0,
},
extensions: [{ type: NeurocogExtension }],
};
@@ -321,9 +316,6 @@ if (Manipulations.numCalibrationOneTrials + Manipulations.numMainTrials > 0) {
Manipulations.numCalibrationOneTrials + Manipulations.numMainTrials
} ` +
`games.
` +
- `You will not be shown if you have correctly ` +
- `answered or not, and you will be asked to rate your ` +
- `confidence after some of the games.
` +
` ` +
`Good luck!
` +
instructionContinueText
@@ -358,7 +350,7 @@ if (_.isEqual(keyLayout.name, "spectrometer")) {
type: InstructionsPlugin,
pages: spectrometer,
allow_keys: !keyLayout.showButtons,
- key_forward: keyLayout.trigger.charAt(keyLayout.trigger.length - 1),
+ key_forward: keyLayout.trigger,
show_clickable_nav: keyLayout.showButtons,
});
} else {
@@ -366,8 +358,8 @@ if (_.isEqual(keyLayout.name, "spectrometer")) {
type: InstructionsPlugin,
pages: main,
allow_keys: !keyLayout.showButtons,
- key_forward: keyLayout.right.charAt(keyLayout.right.length - 1),
- key_backward: keyLayout.left.charAt(keyLayout.left.length - 1),
+ key_forward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["4"] : keyLayout["3"],
+ key_backward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["1"] : keyLayout["2"],
show_page_number: true,
show_clickable_nav: keyLayout.showButtons,
});
@@ -392,7 +384,6 @@ for (let t = 0; t < Manipulations.numCalibrationOneTrials; t++) {
dotDirection: r,
dotVelocity: 2.0,
showFeedback: false,
- checkConfidence: true,
keyLayout: keyLayout,
data: {
name: trialName,
@@ -400,10 +391,9 @@ for (let t = 0; t < Manipulations.numCalibrationOneTrials; t++) {
coherence: k,
stimulusDuration: 1500,
dotDirection: r,
- referenceSelection: "",
+ selection: "",
deviation: deviation,
correct: false,
- confidenceSelection: 0,
},
extensions: [{ type: NeurocogExtension }],
};
@@ -431,7 +421,6 @@ for (let t = 0; t < Manipulations.numMainTrials; t++) {
dotDirection: r,
dotVelocity: 2.0,
showFeedback: false,
- checkConfidence:true,
keyLayout: keyLayout,
data: {
name: trialName,
@@ -439,10 +428,9 @@ for (let t = 0; t < Manipulations.numMainTrials; t++) {
coherence: k,
stimulusDuration: 1500,
dotDirection: r,
- referenceSelection: "",
+ selection: "",
deviation: deviation,
correct: false,
- confidenceSelection: 0,
},
extensions: [{ type: NeurocogExtension }],
};
@@ -462,7 +450,7 @@ timeline.push({
allow_backward: false,
button_label_next: "Finish",
show_clickable_nav: false,
- key_forward: keyLayout.right,
+ key_forward: _.isEqual(keyLayout.name, "spectrometer") ? keyLayout["4"] : keyLayout["3"],
});
jsPsych.run(timeline);
diff --git a/src/plugin.ts b/src/plugin.ts
index 49b10aa..6287658 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -25,6 +25,14 @@ import { IRenderer } from "../types";
import Two from "two.js";
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
+// Global keyup handler for decision input
+declare global {
+ interface Window {
+ decisionKeyUpHandler?: (event: KeyboardEvent) => void;
+ postDecisionKeyUpHandler?: (event: KeyboardEvent) => void;
+ }
+}
+
const info = {
name: "dot-game",
parameters: {
@@ -61,10 +69,6 @@ const info = {
type: ParameterType.INT,
default: undefined,
},
- checkConfidence: {
- type: ParameterType.BOOL,
- default: false,
- },
},
};
@@ -172,18 +176,12 @@ class DotGamePlugin implements JsPsychPlugin {
currentStimulus = stimuli.shift();
// Configure stimulus-specific parameters
- if (currentStimulus.getParameters().name === "reference") {
+ if (currentStimulus.getParameters().name === "decision") {
// Reference stimulus
trial.data.referenceStartTime = performance.now();
// Hide the mouse cursor
graphics.cursorVisibility(false);
- } else if (currentStimulus.getParameters().name === "confidence") {
- // Start a timer if a confidence stimuli is run.
- trial.data.confidenceStartTime = performance.now();
-
- // Show the mouse cursor
- graphics.cursorVisibility(true);
} else if (currentStimulus.getParameters().name === "motion") {
trial.data.stimulusDuration =
currentStimulus.getParameters().timing.run;
@@ -193,12 +191,11 @@ class DotGamePlugin implements JsPsychPlugin {
} else if (currentStimulus.getParameters().name === "initial") {
// Hide the mouse cursor
graphics.cursorVisibility(false);
- } else if (currentStimulus.getParameters().name === "decision") {
- // Increase the run time of fixation cross to 1250ms if feedback
- // is to be shown
- if (trial.showFeedback === true) {
- currentStimulus.getParameters().timing.run = 1250;
- }
+ } else if (currentStimulus.getParameters().name === "post-decision") {
+ // Set up keyup handler to wait for all keys to be released
+ postDecisionKeyUpHandler = createPostDecisionKeyUpHandler();
+ window.postDecisionKeyUpHandler = postDecisionKeyUpHandler;
+ document.addEventListener("keyup", postDecisionKeyUpHandler);
} else {
// Show the mouse cursor
graphics.cursorVisibility(true);
@@ -208,105 +205,178 @@ class DotGamePlugin implements JsPsychPlugin {
Runner.start(currentStimulus);
};
+ // Add variables for key hold functionality
+ let keyHoldTimer: number | null = null;
+ let currentKey: string | null = null;
+ let postDecisionKeyUpHandler: ((event: KeyboardEvent) => void) | null = null;
+
/**
- * An event handler for decision made during a trial
- * @param {KeyboardEvent} event the particular event or keypress
+ * An event handler for keydown during decision input
*/
- const decisionHandler = (event: KeyboardEvent) => {
- // Record the keycode to process the event
- const keycode = event.key;
+ const decisionKeyDownHandler = (event: KeyboardEvent) => {
+ const keycode = event.key.toLowerCase(); // Convert to lowercase for consistent comparison
- if (
- Object.keys(currentStimulus.getParameters().keybindings).includes(
- keycode
- )
- ) {
- // Handle the decision if a valid key has been pressed for this stage
- if (currentStimulus.getParameters().name === "confidence") {
- // Handle 'confidence' stimuli
- const slider = document.getElementById(
- "confidence-slider"
- ) as HTMLInputElement;
- if (slider) {
- // Show the thumb if it is currently hidden when adjusting confidence
- if (
- slider.className === "confidence-slider-hidden" &&
- (keycode === keyLayout.left || keycode === keyLayout.right)
- ) {
- slider.className = "confidence-slider";
- }
+ // Filter out invalid keycodes
+ if (!Object.keys(currentStimulus.getParameters().keybindings).includes(keycode)) {
+ return;
+ }
- // Handle incrementing and decrementing slider
- if (keycode === keyLayout.left) {
- slider.stepDown(1);
- } else if (keycode === keyLayout.right) {
- slider.stepUp(1);
- }
+ // Prevent default to avoid key repeat
+ event.preventDefault();
+
+ // If already holding a key, ignore (prevents multiple simultaneous key holds)
+ if (currentKey !== null) {
+ return;
+ }
- // Ensure we handle a mistake notification
- if (keycode === keyLayout.alt || event.type === "click") {
- // Store mistake boolean
- trial.data.confidenceMistake = true;
+ currentKey = keycode;
+
+ // Find the corresponding button element
+ const buttonMapping = {
+ [keyLayout["1"]]: "vc_l",
+ [keyLayout["2"]]: "sc_l",
+ [keyLayout["3"]]: "sc_r",
+ [keyLayout["4"]]: "vc_r"
+ };
+
+ const buttonId = buttonMapping[keycode];
+ if (buttonId) {
+ const buttonElement = document.querySelector(`[data-button-id="${buttonId}"]`) as HTMLDivElement;
+ if (buttonElement) {
+ // Start the progress bar animation
+ renderer.startProgress(buttonElement);
+ }
+ }
- // Calculate and store confidence data
- trial.data.confidenceEndTime = performance.now();
- trial.data.confidenceTotalTime =
- trial.data.confidenceEndTime - trial.data.confidenceStartTime;
- trial.data.confidenceSelection = slider.value;
+ // Set timer for 2 seconds
+ keyHoldTimer = window.setTimeout(() => {
+ if (currentKey === keycode) {
+ // After 2 seconds, process the decision
+ decisionHandler(event);
+ resetKeyHold();
+ }
+ }, 2000);
+ };
- // Continue to the next Stimulus
- console.warn("Mistake stored in trial data");
- Runner.post(currentStimulus);
- }
+ /**
+ * An event handler for keyup during decision input
+ */
+ const decisionKeyUpHandler = (event: KeyboardEvent) => {
+ if (currentKey === event.key.toLowerCase()) {
+ resetKeyHold();
+ }
+ };
- // Finally, we ignore any submissions if the slider is hidden, only submit if slider is visible
- if (
- keycode === keyLayout.submit &&
- slider.className === "confidence-slider"
- ) {
- // Calculate and store confidence data
- trial.data.confidenceEndTime = performance.now();
- trial.data.confidenceTotalTime =
- trial.data.confidenceEndTime - trial.data.confidenceStartTime;
- trial.data.confidenceSelection = slider.value;
-
- // Continue to the next Stimulus
- Runner.post(currentStimulus);
- }
- } else {
- console.warn("Did not get reference to slider element");
- }
- } else if (currentStimulus.getParameters().name === "reference") {
- // Handle 'reference' stimuli
- selection =
- currentStimulus.getParameters().keybindings[keycode].choice;
- currentStimulus.removeKeybindings();
-
- // Calculate and store reference data
- trial.data.referenceEndTime = performance.now();
- trial.data.referenceTotalTime =
- trial.data.referenceEndTime - trial.data.referenceStartTime;
-
- // Normalize selection data
- trial.data.referenceSelection = selection === "left" ? 1 : 2;
-
- // Normalize correct data
- trial.data.correct = selection === trial.data.deviation ? 1 : 0;
-
- // Increment score if correct
- if (trial.data.correct === 1 && trial.name === "main") {
- trial.data.score++;
+ const resetKeyHold = () => {
+ // Clear the timer
+ if (keyHoldTimer) {
+ clearTimeout(keyHoldTimer);
+ keyHoldTimer = null;
+ }
+
+ // Reset progress bar if there's a current key
+ if (currentKey) {
+ const buttonMapping = {
+ [keyLayout["1"]]: "vc_l",
+ [keyLayout["2"]]: "sc_l",
+ [keyLayout["3"]]: "sc_r",
+ [keyLayout["4"]]: "vc_r"
+ };
+
+ const buttonId = buttonMapping[currentKey];
+ if (buttonId) {
+ const buttonElement = document.querySelector(`[data-button-id="${buttonId}"]`) as HTMLDivElement;
+ if (buttonElement) {
+ // Stop and reset the progress bar
+ renderer.stopProgress(buttonElement);
}
+ }
+ }
- // Continue to the next Stimulus
- Runner.post(currentStimulus);
+ currentKey = null;
+ };
+
+ /**
+ * Handler for post-decision keyup events
+ * Finishes the post-decision stimulus when all keys are released
+ */
+ const createPostDecisionKeyUpHandler = () => {
+ const postDecisionStartTime = performance.now();
+
+ return (event: KeyboardEvent) => {
+ // Check if this is one of our decision keys
+ const keycode = event.key.toLowerCase();
+ const decisionKeys = [keyLayout["1"], keyLayout["2"], keyLayout["3"], keyLayout["4"]];
+
+ if (decisionKeys.includes(keycode)) {
+ // Check if any decision keys are still being held
+ setTimeout(() => {
+ if (currentStimulus && currentStimulus.getParameters().name === "post-decision") {
+ const elapsedTime = performance.now() - postDecisionStartTime;
+ // Minimum display time: 250ms base + 1000ms if feedback is enabled
+ const minDisplayTime = trial.showFeedback === true ? 1250 : 250;
+
+ if (elapsedTime < minDisplayTime) {
+ // Wait for the remaining time before finishing
+ setTimeout(() => {
+ if (currentStimulus && currentStimulus.getParameters().name === "post-decision") {
+ Runner.post(currentStimulus);
+ }
+ }, minDisplayTime - elapsedTime);
+ } else {
+ // Already waited long enough, finish immediately
+ Runner.post(currentStimulus);
+ }
+ }
+ }, 50);
}
- } else {
+ };
+ };
+
+ /**
+ * An event handler for decision made during a trial
+ * @param {KeyboardEvent} event the particular event or keypress
+ */
+ const decisionHandler = (event: KeyboardEvent) => {
+ // Record the keycode to process the event
+ const keycode = event.key.toLowerCase();
+
+ // Filter out invalid keycodes
+ if (!Object.keys(currentStimulus.getParameters().keybindings).includes(
+ keycode
+ )) {
console.warn(
`Invalid key "${keycode}" for stimulus type "${
currentStimulus.getParameters().name
}"`
);
+ return;
+ }
+
+ if (currentStimulus.getParameters().name === "decision") {
+ // Handle 'decision' stimuli
+ selection =
+ currentStimulus.getParameters().keybindings[keycode].choice;
+ currentStimulus.removeKeybindings();
+
+ // Calculate and store reference data
+ trial.data.referenceEndTime = performance.now();
+ trial.data.referenceTotalTime =
+ trial.data.referenceEndTime - trial.data.referenceStartTime;
+
+ // Normalize selection data
+ trial.data.selection = selection;
+
+ // Normalize correct data
+ trial.data.correct = selection.endsWith(trial.data.deviation) ? 1 : 0;
+
+ // Increment score if correct
+ if (trial.data.correct === 1 && trial.name === "main") {
+ trial.data.score++;
+ }
+
+ // Continue to the next Stimulus
+ Runner.post(currentStimulus);
}
};
@@ -452,8 +522,8 @@ class DotGamePlugin implements JsPsychPlugin {
// Display of the reference angle and the coloured arcs for
// user selection.
- const reference = {
- name: "reference",
+ const decision = {
+ name: "decision",
components: [
"outline",
"fixation",
@@ -473,25 +543,33 @@ class DotGamePlugin implements JsPsychPlugin {
post: 0,
},
keybindings: {
- [keyLayout.left]: {
- choice: "left",
- handler: decisionHandler,
+ [keyLayout["1"]]: {
+ choice: "vc_l",
+ handler: decisionKeyDownHandler,
+ },
+ [keyLayout["2"]]: {
+ choice: "sc_l",
+ handler: decisionKeyDownHandler,
},
- [keyLayout.right]: {
- choice: "right",
- handler: decisionHandler,
+ [keyLayout["3"]]: {
+ choice: "sc_r",
+ handler: decisionKeyDownHandler,
+ },
+ [keyLayout["4"]]: {
+ choice: "vc_r",
+ handler: decisionKeyDownHandler,
},
},
target: display_element,
trial: trial,
rendererParameters: rendererParameters,
- eventHandler: decisionHandler,
+ eventHandler: decisionKeyDownHandler,
postTrialHandler: nextStimulus,
};
// Brief display of the fixation cross.
- const decision = {
- name: "decision",
+ const postDecision = {
+ name: "post-decision",
components: ["outline", "fixation"],
two: two,
renderer: renderer,
@@ -500,48 +578,9 @@ class DotGamePlugin implements JsPsychPlugin {
selected: false,
timing: {
pre: 0,
- run: 250,
- post: 0,
- },
- target: display_element,
- trial: trial,
- rendererParameters: rendererParameters,
- eventHandler: decisionHandler,
- postTrialHandler: nextStimulus,
- };
-
- // Display of the confidence slider used to rate confidence.
- const confidence = {
- name: "confidence",
- components: ["confidence"],
- two: two,
- renderer: renderer,
- graphics: graphics,
- interactive: true,
- selected: false,
- timing: {
- pre: 0,
- run: -1,
+ run: -1, // Wait indefinitely until all keys are released
post: 0,
},
- keybindings: {
- [keyLayout.submit]: {
- choice: "submit",
- handler: decisionHandler,
- },
- [keyLayout.left]: {
- choice: "decrease",
- handler: decisionHandler,
- },
- [keyLayout.right]: {
- choice: "increase",
- handler: decisionHandler,
- },
- [keyLayout.alt]: {
- choice: "mistake",
- handler: decisionHandler,
- },
- },
target: display_element,
trial: trial,
rendererParameters: rendererParameters,
@@ -554,19 +593,16 @@ class DotGamePlugin implements JsPsychPlugin {
stimuli.push(
new Stimulus(initial),
new Stimulus(motion),
- new Stimulus(reference),
- new Stimulus(decision)
+ new Stimulus(decision),
+ new Stimulus(postDecision)
);
- if (trial.checkConfidence === true) {
- // Exception for tutorial trials, confidence should be shown
- // for all trials
- stimuli.push(new Stimulus(confidence));
- }
-
let currentStimulus = null;
trial.data.trialStartTime = performance.now();
+ // Make the keyup handler globally accessible
+ window.decisionKeyUpHandler = decisionKeyUpHandler;
+
nextStimulus();
}
}
diff --git a/types/index.d.ts b/types/index.d.ts
index 8eac43d..98ca5d7 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -9,7 +9,6 @@ export type RDKTrial = {
dotDirection: number;
dotVelocity: number;
showFeedback: boolean;
- checkConfidence: boolean;
keyLayout: IKeys;
data: {
name: string;
@@ -17,10 +16,9 @@ export type RDKTrial = {
coherence: number;
stimulusDuration: number;
dotDirection: number;
- referenceSelection: string;
+ selection: "vc_l" | "sc_l" | "sc_r" | "vc_r";
deviation: "left" | "right";
correct: boolean;
- confidenceSelection: number;
};
};