Skip to content
Merged
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

<!-- CC BY-NC-SA 4.0 License -->
Expand Down
47 changes: 47 additions & 0 deletions analysis/v1.8.2_data_dictionary.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "task_rdk",
"version": "1.8.1",
"version": "1.8.2",
"description": "RDK Task",
"scripts": {
"build": "yarn build:desktop",
Expand Down
116 changes: 6 additions & 110 deletions src/classes/Graphics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down Expand Up @@ -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"));
}

/**
Expand All @@ -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"));
}

/**
Expand Down Expand Up @@ -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 += `<div id="confidence-controls-container">`;
// Decrease confidence
html += `<div id="confidence-controls-container-element">`;
html += Renderer.getEmbeddedControllerButton(1);
html += `<p style="font-size: large; font-weight: bold;">Decrease Confidence</p>`;
html += `</div>`;

// Submit
html += `<div id="confidence-controls-container-element">`;
html += `<p style="font-size: large; font-weight: bold;">Continue</p>`;
html += Renderer.getEmbeddedControllerButton(3);
html += `</div>`;

// Increase confidence
html += `<div id="confidence-controls-container-element">`;
html += `<p style="font-size: large; font-weight: bold;">Increase Confidence</p>`;
html += Renderer.getEmbeddedControllerButton(4);
html += `</div>`;
html += `</div>`;
} else {
html += `<div id="confidence-controls-container">`;
// Decrease confidence
html += `<div id="confidence-controls-container-element">`;
html += Renderer.getEmbeddedKeyboardButton("F");
html += `<p style="font-size: large; font-weight: bold;">Decrease Confidence</p>`;
html += `</div>`;

// Submit
html += `<div id="confidence-controls-container-element">`;
html += `<p style="font-size: large; font-weight: bold;">Continue</p>`;
html += Renderer.getEmbeddedKeyboardButton("K");
html += `</div>`;

// Increase confidence
html += `<div id="confidence-controls-container-element">`;
html += `<p style="font-size: large; font-weight: bold;">Increase Confidence</p>`;
html += Renderer.getEmbeddedKeyboardButton("J");
html += `</div>`;
html += `</div>`;
}

// Confidence slider
html += `<div style="margin: 50px 0px; height: 80px; width: 100%; display: flex; justify-content: center; align-items: center;">`;
html += `<div style="position: relative; width: 60vw;">`;
html += `<input type="range" value="70" min="50" max="100" step="10" style="width: 100%;" class="confidence-slider-hidden" id="confidence-slider">`;
html += `</input>`;

// Add labels
const labels = ["50%", "60%", "70%", "80%", "90%", "100%"];
html += `<div>`;
for (let i = 0; i < labels.length; i++) {
const width = 100 / (labels.length - 1);
const offset = i * width - width / 2;
html += `<div style="display: inline-block; position: absolute; left:${offset}%; text-align: center; margin-top: 4vh; width: ${width}%;">`;
html += `<span style="text-align: center; font-size: 1.5em;">${labels[i]}</span>`;
html += `</div>`;
}
html += `</div>`;
html += `</div>`;
html += `<br>`;
html += `</div>`;

// Button to notify of a mistake
html += `<div id="mistake-button-container">`;
if (__TARGET__ === "spectrometer") {
html += `<p style="font-size: large; font-weight: bold">`;
html += `I made a mistake`;
html += `</p>`;
html += Renderer.getEmbeddedControllerButton(2);
} else {
html += `<button id="mistake-button" class="jspsych-btn">`;
html += `I made a mistake`;
html += `</button>`;
html += Renderer.getEmbeddedKeyboardButton("D");
}
html += `</div>`;

// 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
Expand All @@ -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();
}
}
Loading