Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
788dcd3
initial plugin skeleton
LoicGrobol Jun 9, 2025
6e679a5
This is now a light spr plugin
LoicGrobol Jun 10, 2025
fe77c2d
we have maze, captain
LoicGrobol Jun 10, 2025
119da04
fix alignment, add waiting time
LoicGrobol Jun 10, 2025
b23313f
bump readme
LoicGrobol Jun 10, 2025
7d72fa7
fix test
LoicGrobol Jun 11, 2025
c0c6c28
add question
LoicGrobol Jun 13, 2025
8be19bd
the tests! they run!
LoicGrobol Jun 13, 2025
5cb4ae2
make canvas style completely configurable
LoicGrobol Jun 13, 2025
9b7ded0
log question rt
LoicGrobol Jun 16, 2025
10ae309
add option to halt on error
LoicGrobol Jun 16, 2025
3128056
split instruction display and answer loggin logics
LoicGrobol Jun 16, 2025
a45de9b
fix first word, add spaghetti code
LoicGrobol Jun 16, 2025
99bec06
fix step_display
LoicGrobol Jun 16, 2025
aeb4adc
typing
LoicGrobol Jun 16, 2025
45d6115
consolidate the font style in a single parameter
LoicGrobol Jun 16, 2025
39f96ba
start documenting
LoicGrobol Jun 16, 2025
cdb8203
More doc
LoicGrobol Jun 17, 2025
28a1bd8
improve doc types
LoicGrobol Jun 17, 2025
8bb1592
typo
LoicGrobol Jun 17, 2025
6247000
typo
LoicGrobol Jun 18, 2025
baccad5
stop logging word_number
LoicGrobol Jun 18, 2025
dde0c18
fix types in doc
LoicGrobol Jun 18, 2025
56c6eaf
doc typing
LoicGrobol Jun 18, 2025
fc8381e
refactor (more typescript-y) to prepare moving from canvas to DOM
LoicGrobol Jun 18, 2025
ebc47e2
restore font align
LoicGrobol Jun 18, 2025
baa1aeb
fix rt for the first word
LoicGrobol Jun 18, 2025
14e3554
add test for waiting time
LoicGrobol Jun 18, 2025
d463ea1
add inter-word and before-question interval
LoicGrobol Jun 18, 2025
09a23ab
start working on dom port
LoicGrobol Jun 18, 2025
69e5424
pure dom display
LoicGrobol Jun 18, 2025
34e49ce
split off style and remove obsolete params
LoicGrobol Jun 18, 2025
168c4e3
cleanup
LoicGrobol Jun 18, 2025
5d3753c
move all style to example
LoicGrobol Jun 18, 2025
8a17207
move the questions to html
LoicGrobol Jun 18, 2025
a49d204
remove refs to end_interval
LoicGrobol Jun 18, 2025
a36af8d
Merge pull request #1 from LoicGrobol/maze-dom
LoicGrobol Jun 19, 2025
ea8ea3a
fix question eval in the example
LoicGrobol Jun 19, 2025
fe50cdf
fix question data
LoicGrobol Jun 19, 2025
e2aaeb1
use compareKeys to compare keys
LoicGrobol Jun 19, 2025
a44d89b
remove obsolete param
LoicGrobol Jun 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,243 changes: 1,036 additions & 207 deletions package-lock.json

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions packages/plugin-maze/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Maze

## Overview

A jsPsych plugin for running Maze experiments

## Loading

### In browser

```js
<script src="https://unpkg.com/@jspsych-contrib/[email protected]"></script>
```

### Via NPM

```
npm install @jspsych-contrib/plugin-maze
```

```js
import jsPsychMaze from '@jspsych-contrib/plugin-maze';
```

## Compatibility

jsPsych 8.0.0

## Documentation

See [documentation](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-maze/docs/jspsych-maze.md)

## Author / Citation

[Morgan Grobol](https://lgrobol.bzh)
90 changes: 90 additions & 0 deletions packages/plugin-maze/docs/maze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
Maze
====

A jsPsych plugin for running Maze (Forster et al., 2009) experiments, a version of self-paced
reading that asks to chose between the correct next word and a distractor.

## Parameters

In addition to the [parameters available in all plugins](https://www.jspsych.org/latest/overview/plugins/#parameters-available-in-all-plugins), this plugin accepts the following parameters. Parameters with a default value of undefined must be specified. Other parameters can be left unspecified if the default value is acceptable.

| Parameter | Type | Default Value | Description |
|-------------------|---------------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
| `sentence` | `Array<[string, string]>` | `undefined` | The sentence to use in the Maze, as `[word, foil]` pairs. |
| `question` | `{text: string, correct: str, wrong: str}?` | `null` | An optional question to ask at the end of the mazz to make sure the subject has read the sentence. |
| `canvas_style` | `string` | `"border: 0px solid black;"` | Extra style to inject in the canvas element. |
| `canvas_colour` | `string` | `"white"` | The canvas' background colour. |
| `canvas_size` | `[number, number]` | `[1280, 960]` | The dimensions of the canvas. |
| `font_colour` | `str` | `"black"` | The font colour for the text. |
| `font_style` | `str` | `"normal 24px monospace"` | The font style for the text. |
| `halt_on_error` | `bool` | `false` | If true, any error ends the trial and sends the subject directly to the question (if any), then exit. |
| `keys` | `{left: string, right: string}` | `{left: "f", right: "j"}` | The choice/navigation keys. |
| `position_left` | `{x: number?, y: number?}` | `{x: null, y: null}` | The position of the left word. A null `x` is set to 1/3 of the canvas' width and null `y` is set to half of the canvas' height. |
| `position_right` | `{x: number?, y: number?}` | `{x: null, y: null}` | The position of the right word. A null `x` is set to 2/3 of the canvas' width and null `y` is set to half of the canvas' height. |
| `tranlate_origin` | `bool` | `true` | If true, the coordinates are transposed to have `(0, 0)` be the center of the canvas. |
| `waiting_time` | `number` | `0` | The minimum time (in ms) before the subject is allowed to chose a word. |

## Data Generated

In addition to the [default data collected by all plugins](https://www.jspsych.org/latest/overview/plugins/#data-collected-by-all-plugins), this plugin collects the following data for each trial.

| Name | Type | Value |
|------------|---------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| `sentence` | `string` | The sentence used in the trial (joined with spaces) |
| `events` | `Array<{correct: bool, foil: string, rt: number, word: string, word_number: number}>` | The parameters, choice and interaction time for each word of the sentence |
| `question` | `{question: {text: string, correct: str, wrong: str}, correct: bool, rt: number}>` | The parameters, choice and interaction time for the final question (if any) |

## Install

Using the CDN-hosted JavaScript file:

```js
<script src="https://unpkg.com/@jspsych-contrib/plugin-maze"></script>
```

Using the JavaScript file downloaded from a GitHub release dist archive:

```js
<script src="jspsych/plugin-maze.js"></script>
```

Using NPM:

```bash
npm install @jspsych-contrib/plugin-maze
```

```js
import Maze from '@jspsych-contrib/plugin-maze';
```

## Examples

```javascript

const trial = {
type: jsPsychMaze,
sentence: [
["After", "x-x-x"],
["a", "so"],
["bit", "pot"],
["of", "if"],
["success", "singing"],
["the", "ate"],
["stocks", "winter"],
["took", "walk"],
["a", "we"],
["dive", "toad"],
],
question: {
text: "Did the stocks take a dive?",
correct: "yes",
wrong: "no",
},
waiting_time: 200,
}
```

## Bibliography

- Forster, Kenneth I., Christine Guerrera, and Lisa Elliot. 2009. ‘The Maze Task: Measuring Forced Incremental Sentence Processing Time’. Behavior Research Methods 41 (1): 163–71. <https://doi.org/10.3758/BRM.41.1.163>.
36 changes: 36 additions & 0 deletions packages/plugin-maze/examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/jspsych"></script>
<!-- The plugin is loaded here -->
<script src="https://unpkg.com/@jspsych/plugin-maze"></script>
<script src="../dist/index.browser.js"></script>

<!-- Ugly trick to keep the data separate while not having to think of how to fetch it -->
<script src="stimuli.js"></script>

<link rel="stylesheet" href="https://unpkg.com/jspsych/css/jspsych.css" />
</head>

<body></body>
<script>
const jsPsych = initJsPsych();

const procedure = {
timeline : [
{
type: jsPsychMaze,
sentence: jsPsych.timelineVariable('sentence'),
question: jsPsych.timelineVariable('question'),
waiting_time: 200,
on_finish: () => {
jsPsych.data.displayData('json');
},
}
],
timeline_variables: data,
};

jsPsych.run(procedure)
</script>
</html>
39 changes: 39 additions & 0 deletions packages/plugin-maze/examples/stimuli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const data = [
{
sentence: [
["After", "x-x-x"],
["a", "so"],
["bit", "pot"],
["of", "if"],
["success", "singing"],
["the", "ate"],
["stocks", "winter"],
["took", "walk"],
["a", "we"],
["dive", "toad"],
],
question: {
text: "Did the stocks take a dive?",
correct: "yes",
wrong: "no",
},
},
{
sentence: [
["The", "x-x-x"],
["fashion", "realize"],
["model", "shirt"],
["was", "see"],
["very", "into"],
["self-conscious", "professionally"],
["of", "do"],
["her", "why"],
["appearance", "sandwiches"],
],
question: {
text: "Who was self-conscious?",
correct: "The model",
wrong: "The designer",
},
},
];
1 change: 1 addition & 0 deletions packages/plugin-maze/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("@jspsych/config/jest").makePackageConfig(__dirname);
50 changes: 50 additions & 0 deletions packages/plugin-maze/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@jspsych-contrib/plugin-maze",
"version": "0.0.1",
"description": "A jsPsych plugin for running Maze experiments",
"type": "module",
"main": "dist/index.cjs",
"exports": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"typings": "dist/index.d.ts",
"unpkg": "dist/index.browser.min.js",
"files": [
"src",
"dist"
],
"source": "src/index.ts",
"scripts": {
"test": "jest",
"test:watch": "npm test -- --watch",
"tsc": "tsc",
"build": "rollup --config",
"build:watch": "npm run build -- --watch",
"dev-serve": "concurrently \"npm run build -- --watch\" \"serve\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/jspsych/jspsych-contrib.git",
"directory": "packages/plugin-maze"
},
"author": {
"name": "Morgan Grobol",
"url": "https://lgrobol.bzh"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/jspsych/jspsych-contrib/issues"
},
"homepage": "https://github.com/jspsych/jspsych-contrib/tree/main/packages/plugin-maze",
"peerDependencies": {
"jspsych": ">=8.0.0"
},
"devDependencies": {
"@jspsych/config": "^3.2.2",
"@jspsych/test-utils": "^1.0.0",
"concurrently": "^9.1.2",
"jspsych": "^8.0.0",
"serve": "^14.2.4"
}
}
3 changes: 3 additions & 0 deletions packages/plugin-maze/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { makeRollupConfig } from "@jspsych/config/rollup";

export default makeRollupConfig("jsPsychMaze");
77 changes: 77 additions & 0 deletions packages/plugin-maze/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { pressKey, startTimeline } from "@jspsych/test-utils";

import jsPsychMaze from ".";

jest.useFakeTimers();

describe("The maze jspsych plugin", () => {
it("Runs maze", async () => {
const sentence = [
["The", "x-x-x"],
["fashion", "realize"],
["model", "shirt"],
["was", "see"],
["very", "into"],
["self-conscious", "professionally"],
["of", "do"],
["her", "why"],
["appearance", "sandwiches"],
];
const { expectFinished, getHTML, getData, displayElement, jsPsych } = await startTimeline([
{
type: jsPsychMaze,
sentence: sentence,
},
]);

// Start
jest.advanceTimersByTime(100);
pressKey("f");

for (const [_word, _foil] of sentence) {
jest.advanceTimersByTime(100);
pressKey("f");
}

await expectFinished();
});
it("Asks questions", async () => {
const sentence = [
["The", "x-x-x"],
["fashion", "realize"],
["model", "shirt"],
["was", "see"],
["very", "into"],
["self-conscious", "professionally"],
["of", "do"],
["her", "why"],
["appearance", "sandwiches"],
];
const { expectFinished, getHTML, getData, displayElement, jsPsych } = await startTimeline([
{
type: jsPsychMaze,
sentence: sentence,
question: {
text: "Who was self-conscious?",
correct: "The model",
wrong: "The designer",
},
},
]);

// Start
jest.advanceTimersByTime(100);
pressKey("f");

for (const [_word, _foil] of sentence) {
jest.advanceTimersByTime(100);
pressKey("f");
}

// Question
jest.advanceTimersByTime(100);
pressKey("f");

await expectFinished();
});
});
Loading