Table of contents
Implementing your own experiment type
An experiment type is responsible for several things:
- Generating presentable stimuli (e.g., images) based on user-defined materials (e.g., texts).
- Configuring experiment stages, which define what is shown on screen and how the user can interact.
- Defining the order of experiment stages for each participant in session files.
Understanding stages and sessions
Experiment sessions are defined in JSON files in the sessions folder. These files are generated by the experiment type during eidon build.
Let’s look at an excerpt from a session definition:
{
"stages": [
{"$name": "setup", "$type": "Setup"},
{
"$type": "StimulusMultiPage",
"$name": "instructions",
"$record_eyes": true,
"imgpaths": ["stimuli/instructions.0.png", "stimuli/instructions.1.png"],
"next_page_key": "SPACE"
},
{
"$name": "wait",
"$type": "HostControlled",
"continue_key": "ESCAPE",
"setup_key": "SPACE",
"stage": {
"$type": "StimulusPage",
"imgpath": "stimuli/wait.participant.png",
"continue_key": "SPACE"
},
"host_imgpath": "stimuli/wait.host.png"
},
{"$name": "practice.1.drift", "$type": "DriftCorrect", "location": [25, 437]},
{
"$type": "StimulusPage",
"$name": "practice.1.text",
"$record_eyes": true,
"imgpath": "stimuli/practice.1.text.png",
"continue_key": "SPACE"
},
{
"$name": "practice.1.question.1",
"$type": "MultipleChoiceQuestion",
"$record_eyes": true,
"imgpath": "stimuli/practice.1.question.1.png",
"option_keys": ["F", "J"],
"correct_option_index": null,
"option_boxes": [[50, 537.5, 500.0, 87.5], [550.0, 537.5, 500.0, 87.5]],
"confirm_key": null
},
...
]
}
As you can see, a session essentially consists of a list of stages that will be presented in order. Think of these as “screens” (although some stages like StimulusMultiPage can include multiple screens or pages). The parameters starting with $ can be applied to any stage:
"$type"(required): The stage type that defines what happens in this stage. See here for an overview of available stage types."$name": The name of the stage, which will appear in thestagecolumn of recorded gaze files."$record_eyes": Whether eye-tracking data should be recorded during this stage (default:false)."$record_audio": Whether audio should be recorded during this stage (default:false).
Some stage types, like Setup, are very simple:
{"$type": "Setup", "$name": "setup"}
This stage simply activates the eye tracker’s setup mode, allowing the experimenter to perform calibration.
Other stage types have more specific configuration options, like StimulusPage:
{
"$type": "StimulusPage",
"$name": "practice.1.text",
"$record_eyes": true,
"imgpath": "stimuli/practice.1.text.png",
"continue_key": "SPACE"
}
This stage shows a stimulus image and waits until the participant presses a key. imgpath defines the image path, continue_key defines the key.
Some stages even allow nesting other stages inside of it, like HostControlled:
{
"$type": "HostControlled",
"$name": "wait",
"continue_key": "ESCAPE",
"setup_key": "SPACE",
"stage": {
"$type": "StimulusPage",
"imgpath": "stimuli/wait.participant.png",
"continue_key": "SPACE"
},
"host_imgpath": "stimuli/wait.host.png"
}
This stage prompts the participant to wait and hands control over to the experimenter (i.e., the host PC). It shows an image on the host PC and runs any stage on the display PC (in this case, a StimulusPage). Note that since the HostControlled stage prevents user input from the display PC, the continue_key parameter in the inner StimulusPage stage (SPACE) will not have any effect. Instead, the stage waits for the continue_key from the outer stage (ESCAPE) to continue to the next stage.
Refer to this page for detailed information on available stage types and parameters.
How to implement an experiment type
An experiment type is defined as a Python data class with attributes for configuration options and a build() method:
from dataclasses import dataclass
from eidon.build import ExperimentType
@dataclass(kw_only=True)
class MyExperimentType(ExperimentType):
num_participants: int
font_size: int = 25
continue_key: str = "SPACE"
def build(self, experiment_path: Path) -> dict[str, dict[str, Any]]:
...
return {
"P1": {"stages": [...]},
"P2": {"stages": [...]},
...
}
The values for the attributes can be configured in the config.yaml file, along with the name of the experiment type’s Python class:
name: my-experiment
type: MyExperimentType
num_participants: 10
continue_key: RIGHT
The build() method has two jobs:
- Compile stimuli and save them in the
stimulidirectory underexperiment_path. - Return a dictionary of session definitions. The keys are session names, which will be used in the session file names (e.g.,
sessions/P1.json).
Compiling stimuli
Any method can be used to design stimuli (e.g., drawing and saving images using Pillow). For drawing text, the eidon.build.stimuli module provides a few useful helper functions. For example, to generate an image that contains a block of text:
from eidon.build import stimuli
image, = stimuli.generate_text_pages(
text="Hello world",
width=400,
height=300,
margin=20,
font_path=experiment_path / "fonts" / "my_font.ttf",
font_size=25,
)
image.save(experiment_path, "my_stimulus")
stimuli.generate_text_pages() returns a list of TextImage objects, which contains both the drawn image and the areas of interest for characters and words. image.save() saves all of these in the stimuli directory (stimuli/my_stimulus.png, stimuli/my_stimulus.word.csv, etc.).
Take a look at the pre-implemented experiment types for more examples.
Where to put your code
Custom experiment type implementations should be placed inside the experiment folder, in a directory named code. This makes it easy to bundle the custom code together with the remaining material and publish it for reproducibility:
📂 my_experiment
├─ config.yaml
├─ 📂 code
│ └─ 📄 my_custom_type.py
└─ 📁 materials