PsychoPy Coder Tutorial
Getting Started
psychopy-bids introduces three core objects for Coder workflows: BIDSHandler, BIDSBehEvent, and BIDSTaskEvent.
BIDSHandler manages dataset creation, metadata files, and writing events to BIDS-valid output files. BIDSBehEvent and BIDSTaskEvent represent individual rows in your event table and validate required fields.
In the following Python code, we initialize a BIDS dataset and add a few task events.
We are using seedir to display the directory structure. Install it with:
import seedir as sd
from psychopy_bids import bids
subject = {"participant_id": "01", "sex": "male", "age": 20}
handler = bids.BIDSHandler(
dataset="example_dataset",
subject=subject["participant_id"],
session="1",
task="example",
runs=True,
)
handler.createDataset()
start = bids.BIDSTaskEvent(onset=0, duration=0, trial_type="start", text="Let's get started!", color="black")
presentation = bids.BIDSTaskEvent(onset=0.5, duration=5, trial_type="presentation", text="stimulus", color="green")
stop = bids.BIDSTaskEvent(onset=10, duration=0, trial_type="stop", text="We are done!", color="black")
handler.addEvent(start)
handler.addEvent(presentation)
handler.addEvent(stop)
handler.writeEvents(participant_info=subject, execute_sidecar=False)
sd.seedir("example_dataset")
This creates a BIDS dataset. Key outputs look like this (additional helper files can be present):
example_dataset/
├── .bidsignore
├── CHANGES
├── LICENSE
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
├── requirements.txt
├── code/
│ └── example_script.py
└── sub-01/
└── ses-1/
└── beh/
└── sub-01_ses-1_task-example_run-001_events.tsv
The .tsv looks as follows:
| onset |
duration |
trial_type |
| 0.0 |
0 |
start |
| 0.5 |
5 |
presentation |
| 10.0 |
0 |
stop |
In the following tutorial we show how to add these objects to existing experiment code.
Stroop Task
In this example, we modify an existing Stroop task by adding code to store output event data according to BIDS. The pattern can be integrated into any existing PsychoPy + Python experiment script.
The underlying experiment was developed by Dennis Wambacher in the course of the seminar Methods of Technical Experiment Control using Python and PsychoPy. Before you begin, download the base code here.
Step 1: Creating a BIDS Dataset
To obtain a BIDS dataset directly from the experiment we have to collect the right metadata during the experiment and pass it on to the BIDSHandler to create our dataset.
For this experiment, the participant data is collected from the dialog box. The only required field for BIDS is the participant id. Here we also add participant age and sex.
| # Retrieve the Participant Information
participant_info = {"participant_id": v_dialogObj.data[0], "age": v_dialogObj.data[2], "sex": v_dialogObj.data[3]}
|
Next, initialize BIDSHandler. The required argument is the dataset name. In practice, you should also provide participant ID (subject), session, task, and data_type so output files are placed in the correct BIDS path. createDataset() checks whether the expected structure already exists and creates missing files/folders.
psychopy-bids normalizes entities for you (for example, subject 01 becomes sub-01, session 1 becomes ses-1, task stroopbids becomes task-stroopbids).
| # Create Dataset = Directory structure
bids_example = BIDSHandler(
dataset="StroopBids",
subject=participant_info["participant_id"],
session=v_dialogObj.data[1],
task="stroopbids",
data_type="beh" # defines the dataset folder structure, could also be "func"
)
bids_example.createDataset()
|
Then we initialize an empty list where we'll write our BIDS events before passing it to the BIDSHandler at the end of the experiment.
| # Create Event List for the BIDSHandler
events=[]
|
Step 2: Identify all events
Before we start creating the events, we need to define all the events and what information about them is required.
Anything that happens in a participant's environment is part of their cognitive state, even if it is not directly relevant to the research question. To ensure that no events are missed, consider the following: Events that are not part of an experimental design can be things like the presentation of "get ready" before a trial, a fixation cross between stimuli, or feedback. In addition to logging the events themselves, it is important to also log their properties, such as color and position.
The entities identified for our event file are:
onset:
For each event, the start time in seconds must be logged.
duration:
The duration of an event in seconds. In this case, for the instruction and stimuli, the duration is equal to the reaction time. Since the duration of a keystroke (the response) is not measured, it will be marked as "n/a".
event_role:
The role of the event in the experiment. In this case, we have instructions, responses, fixations, stimuli, and feedback.
word:
Here we describe the presented event in more detail. In this example, we add a description of the instruction, the ''+'' presented as fixation, the word presented, and the specific feedback that was presented.
color:
Our stimuli and feedback are presented in different colors, so we append a special column to specify the color in which the event was presented. For consistency, we also add it for the instructions.
trial_type:
Our experiments consists of two different types of trials: congruent and incongruent, since the color in which the word is presented is either congruent or incongruent with the presented word. It is important to note that the response and feedback are also part of a trial, as the stimulus, response, and feedback form a unit.
trial_number:
We add trial numbers in addition to trial_type. This is convenient for trial-level analysis later.
pressed_key:
The particular key pressed by the participant.
response_time:
The time taken to respond to a stimulus in seconds.
response_accuracy:
Whether the correct response within that trial was given or not.
Step 3: Create the BIDSTaskEvents in Code
Now that we have identified all the events to log in Step 2, we can start adding the BIDSTaskEvents for each event in the code.
BIDSTaskEvents takes the input for an event, i.e. one line in the events.tsv file, and formats it. The minimum required input is the "onset" in seconds (integer or float) and the "duration". As previously stated in Step 2, additional information about an event is necessary. To provide this information, customized columns can be created. Note that custom columns can be named freely but we recommend following BIDS guidelines and use snake case.
We will go through our events in chronological order:
1. Instruction
| # STEP 3.1: Add the Bids Event Instruction
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=v_reactionTime,
event_role = "instruction",
word= "instruction_text",
color="black",
pressed_key="space",
response_time=v_reactionTime
)
events.append(bids_event)
|
Note: If custom columns do not apply to a specific event, they may be omitted. psychopy-bids writes those missing values as n/a.
2. Response to the Instruction
| # STEP 3.2: Add the Bids Event Response
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"]+v_reactionTime,
duration = "n/a", # we do not measure the length of the keypress
event_role = "response",
pressed_key="space",
response_time=v_reactionTime
)
events.append(bids_event)
|
3. Fixation Cross
| # STEP 3.3: Add the Bids Event Fixation Cross
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=0.5,
event_role = "fixation",
word=v_item["Word"],
color=v_item["Color"]
)
events.append(bids_event)
|
4. Stimulus
| # STEP 3.4: Add the Bids Event Stimulus
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=stim_duration,
trial_type=v_item["Trial"],
trial_number=v_item["ItemNr"],
event_role = "stimulus",
word=v_item["Word"],
color=v_item["Color"],
pressed_key=v_keyPress[0],
response_time=v_reactionTime,
response_accuracy=v_correct
)
events.append(bids_event)
|
5. Response to Stimulus
If there was a key press.
| # STEP 3.5: Add the Bids Event Response
if not v_reactionTime == "n/a":
bids_event = BIDSTaskEvent(
onset= onset_dict["onset"] + v_reactionTime,
duration="n/a", # We don't measure the length of the keypress
trial_type=v_item["Trial"],
trial_number=v_item["ItemNr"],
event_role = "response",
word=v_item["Word"],
color=v_item["Color"],
pressed_key=v_keyPress[0],
response_time=v_reactionTime,
response_accuracy=v_correct
)
events.append(bids_event)
|
6. Feedback to Response
| # STEP 3.6: Add the Bids Event Feedback
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=1.0,
trial_type=v_item["Trial"],
trial_number=v_item["ItemNr"],
event_role="feedback",
word=feedback_text,
color=feedback_color
)
events.append(bids_event)
|
7. Instruction at the End
| # STEP 3.7: Add Bids Event Instruction for the End Screen
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=onset_dict["onset"]+v_reactionTime,
event_role = "instruction",
word="end_text",
color="black",
pressed_key="space",
response_time=v_reactionTime
)
events.append(bids_event)
|
8. Response to Instruction
| # STEP 3.8: Add Bids Event Response for the Final Key Press
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"]+v_reactionTime,
duration = "n/a", #we do not measure the length of the keypress
event_role = "response",
pressed_key="space",
response_time=v_reactionTime
)
events.append(bids_event)
|
Note: To demonstrate the difference in method and output, we retained the csv file of the initial base code to log the events. It is crucial to ensure that the input order for all columns is accurate when using the csv file method. The code lines responsible for generating entries in the csv file could have been removed after the BIDS events were inserted.
Step 4: Add a json sidecar
The events.tsv file should be accompanied by an events.json sidecar that explains the columns in the .tsv file (see BIDS).
In this tutorial, we use an events_template.json sidecar template that psychopy-bids uses to generate the run-specific sidecar.
In our case, events_template.json looks like this:
{
"onset": {
"LongName": "",
"Description": "Onset of the event.",
"Units": "Seconds"
},
"duration": {
"LongName": "",
"Description": "Duration of the presented event.",
"Units": "Seconds"
},
"event_role": {
"LongName": "",
"Description": "The role of the event in the experiment."
},
"word": {
"LongName": "",
"Description": "Text of the presented event."
},
"color": {
"LongName": "",
"Description": "Color of the presented event."
},
"pressed_key": {
"LongName": "",
"Description": "The pressed response key."
},
"trial_type":{
"LongName": "",
"Description": "Type of experimental trial for the experimental condition",
"Levels": {
"congruent": "Word and color match",
"incongruent": "Word and color do not match"
}
},
"response_time": {
"LongName": "",
"Description": "The response time until a key was pressed after the stimulus onset.",
"Units": "Seconds"
},
"trial_number": {
"LongName": "",
"Description": "Number of the trial",
"Units": "Integers"
},
"response_accuracy": {
"LongName": "",
"Description": "Whether the correct response to a stimulus was given or not",
"Levels": {
"correct": "The correct response was given",
"wrong": "The wrong response was given",
"missing": "No response was given"
}
}
}
To use the template, we set it as an existing file for the BIDSHandler:
| #%% STEP 4: Use events_template.json
# Set path to an existing sidecar JSON file (if one exists)
existing_file = "events_template.json"
|
Step 5: Write BIDS events
At the very end of our experiment code (or if we exit the experiment earlier), we pass the participant information and our list of events to the BIDSHandler. This adds the events.tsv file and the participant file entry. We also create the events.json sidecar file with the template of step 4.
| #%% STEP 5: Save the Event File and Close
bids_example.addEvent(events)
bids_example.writeEvents(participant_info, execute_sidecar=existing_file)
|
Step 6: Check final Stroop task with added BIDS events, run experiment and check output
After implementing all the steps discussed in the base code, the final code should resemble this:
| import os
import psychopy
from psychopy import visual, core, logging, event, gui, data
from psychopy_bids.bids import BIDSHandler, BIDSTaskEvent
# Simple true/false Stroop Task based on Code by Dennis Wambacher
# In a Stroop Task, color names are presented in different colors.
# The participant must decide whether the color name and the color in which it is displayed match
# by pressing the left (mismatch) and right (match) arrow keys.
#%% Set up paths
# Change Working Directory to Current File
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Create a Folder Named Data to Save the log File in
if not os.path.isdir("data"):
os.makedirs("data")
#%% Create Events
# Item Inventory / Item Dictionary with Nr., Word, Color, Correct Answer, and Trial Type.
v_itemDict = [
{"ItemNr": 1, "Word": "Red", "Color": "green", "CorrAnsw": "left", "Trial": "incongruent"},
{"ItemNr": 2, "Word": "Blue", "Color": "blue", "CorrAnsw": "right", "Trial": "congruent"},
{"ItemNr": 3, "Word": "Red", "Color": "red", "CorrAnsw": "right", "Trial": "congruent"},
{"ItemNr": 4, "Word": "Green", "Color": "blue", "CorrAnsw": "left", "Trial": "incongruent"},
{"ItemNr": 5, "Word": "Red", "Color": "red", "CorrAnsw": "right", "Trial": "congruent"},
{"ItemNr": 6, "Word": "Blue", "Color": "red", "CorrAnsw": "left", "Trial": "incongruent"},
{"ItemNr": 7, "Word": "Green", "Color": "red", "CorrAnsw": "left", "Trial": "incongruent"},
{"ItemNr": 8, "Word": "Green", "Color": "green", "CorrAnsw": "right", "Trial": "congruent"},
{"ItemNr": 9, "Word": "Blue", "Color": "blue", "CorrAnsw": "right", "Trial": "congruent"},
]
# Variables for Instruction Text and End Text
v_instrText = "Press the left arrow key if the word and color don't match."
v_instrText += "\n\nPress the right arrow key if the word and color match."
v_instrText += "\n\n\nPress space to start the experiment"
v_finishText = "Thank you for your participation.\n\n\n"
v_finishText += "Press space to end the experiment and close the program"
#%% Get Your Participant Information
# Creating a Dialog Object for Participant Info and Check for Ok
personal_info = {"Participant Code": "", "Session": "", "Age": "", "Sex": ["", "f", "m", "d"]}
v_dialogObj = gui.DlgFromDict(personal_info, title = "StroopBids: Personal Info", order = ["Participant Code", "Session", "Age", "Sex"])
# Create csv File as log File
if v_dialogObj.OK:
v_logFileName = "data/%s_%s.csv" % (v_dialogObj.data[0],v_dialogObj.data[1])
v_logFileObj = logging.LogFile(f=v_logFileName, filemode="w")
# Write the column labels into the log file
v_logFileObj.write("onset;duration;event_role;word;color;pressed_key;trial_type;response_time;trial_number;response_accuracy\n")
else:
core.quit()
#%% STEP 1: Create Bids Dataset
# Retrieve the Participant Information
participant_info = {"participant_id": v_dialogObj.data[0], "age": v_dialogObj.data[2], "sex": v_dialogObj.data[3]}
# Create Dataset = Directory structure
bids_example = BIDSHandler(
dataset="StroopBids",
subject=v_dialogObj.data[0],
session=v_dialogObj.data[1],
task="stroopbids",
data_type="beh" # defines the dataset directory structure, could also be "func"
)
bids_example.createDataset()
# Create Event List for the BIDSHandler
events=[]
#%% Create Objects for Experiment Presentation
# Create a Window for the Text
v_winObj = visual.Window(size=[2560, 1080], color="white", fullscr=True)
v_textObj = visual.TextStim(v_winObj, text="", color="black")
# Create Onset Dictionary for Getting Exact Timings
onset_dict =dict.fromkeys(["onset", "event_onset"])
# Create a Clock for the Response Times
v_reactionClock = core.Clock()
# Create a Cursor(Mouse)-Object to Turn it Invisible During the Experiment
v_mouseObj = event.Mouse(v_winObj)
v_mouseObj.setVisible(False)
#%% Start Experiment Presentation
# Show Instructions until Key Press
v_textObj.setText(v_instrText)
v_textObj.setHeight(0.06)
v_textObj.draw()
v_winObj.timeOnFlip(onset_dict, "onset") # get exact time of presentation
v_winObj.flip()
v_reactionClock.reset() # reset reaction clock to get the time difference between stimulus and response
event.waitKeys(keyList=["space"])
v_reactionTime = v_reactionClock.getTime()
# STEP 3.1: Add the Bids Event Instruction
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=v_reactionTime,
event_role = "instruction",
word= "instruction_text",
color="black",
pressed_key="space",
response_time=v_reactionTime,
)
events.append(bids_event)
# STEP 3.2: Add the Bids Event Response
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"]+v_reactionTime,
duration = "n/a", # we do not measure the length of the keypress
event_role = "response",
pressed_key="space",
response_time=v_reactionTime,
)
events.append(bids_event)
# Write the Instruction into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"],v_reactionTime,"instruction","instruction_text","black","space","n/a",v_reactionTime,"n/a","n/a"))
# Write the Response into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"]+v_reactionTime,"n/a","response","n/a","n/a","space","n/a",v_reactionTime,"n/a","n/a"))
#%% The experiment loop
for v_item in v_itemDict:
# A Fixation Cross for 0.5 Seconds
v_textObj.setText("+")
v_textObj.setColor("black")
v_textObj.setHeight(0.3)
v_textObj.draw()
v_winObj.timeOnFlip(onset_dict, "onset")
v_winObj.flip()
core.wait(0.5)
# STEP 3.3: Add the Bids Event Fixation Cross
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=0.5,
event_role = "fixation",
word=v_item["Word"],
color=v_item["Color"],
)
events.append(bids_event)
# Write the Fixation into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"],0.5,"fixation","n/a","n/a","n/a","n/a","n/a","n/a","n/a"))
# Present the Stimulus Word
v_textObj.setText(v_item["Word"])
v_textObj.setColor(v_item["Color"])
v_textObj.setHeight(0.2)
v_textObj.draw()
v_winObj.timeOnFlip(onset_dict, "onset")
v_winObj.flip()
v_reactionClock.reset()
# Wait 1 Seconds for a Key Press
v_keyPress = event.waitKeys(maxWait=1.0, keyList=["left", "right", "escape"])
v_reactionTime = v_reactionClock.getTime()
# Get the Key and its Correctness
if v_keyPress:
stim_duration = v_reactionTime
if v_keyPress[0] == "escape":
v_winObj.close()
# Store the Event File before Quitting
bids_example.addEvent(events)
bids_example.writeEvents(participant_info)
core.quit()
elif v_keyPress[0] == v_item["CorrAnsw"]:
v_correct = "correct"
feedback_text = "Correct!"
feedback_color = "green"
else:
v_correct = "wrong"
feedback_text = "Wrong!"
feedback_color = "red"
else:
stim_duration = 1.0
feedback_text = "Too Slow!"
feedback_color = "orange"
v_correct = "missing"
v_reactionTime = "n/a"
v_keyPress = ["none"]
# STEP 3.4: Add the Bids Event Stimulus
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=stim_duration,
trial_type=v_item["Trial"],
trial_number=v_item["ItemNr"],
event_role = "stimulus",
word=v_item["Word"],
color=v_item["Color"],
pressed_key=v_keyPress[0],
response_time=v_reactionTime,
response_accuracy=v_correct
)
events.append(bids_event)
# Write the Stimulus into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"],stim_duration,"stimulus",v_item["Word"],v_item["Color"],v_keyPress[0],v_item["Trial"],v_reactionTime,v_item["ItemNr"],v_correct))
# STEP 3.5: Add the Bids Event Response
if not v_reactionTime == "n/a":
bids_event = BIDSTaskEvent(
onset= onset_dict["onset"] + v_reactionTime,
duration="n/a", # We don't measure the length of the keypress
trial_type=v_item["Trial"],
trial_number=v_item["ItemNr"],
event_role = "response",
word=v_item["Word"],
color=v_item["Color"],
pressed_key=v_keyPress[0],
response_time=v_reactionTime,
response_accuracy=v_correct
)
events.append(bids_event)
# Write the Response into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"]+v_reactionTime,"n/a","response",v_item["Word"],v_item["Color"],v_keyPress[0],v_item["Trial"],v_reactionTime,v_item["ItemNr"],v_correct))
# Display the Feedback for 1 Second
v_textObj.setText(feedback_text)
v_textObj.setColor(feedback_color)
v_textObj.draw()
v_winObj.timeOnFlip(onset_dict, "onset")
v_winObj.flip()
core.wait(1.0)
# STEP 3.6: Add the Bids Event Feedback
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=1.0,
trial_type=v_item["Trial"],
trial_number=v_item["ItemNr"],
event_role="feedback",
word=feedback_text,
color=feedback_color
)
events.append(bids_event)
# Write the Feedback into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"],1.0,"feedback",feedback_text,feedback_color,"n/a",v_item["Trial"],"n/a",v_item["ItemNr"],"n/a"))
#%% End the Experiment
# Show the End Text
v_textObj.setText(v_finishText)
v_textObj.setColor("black")
v_textObj.setHeight(0.06)
v_textObj.draw()
v_winObj.timeOnFlip(onset_dict, "onset")
v_winObj.flip()
v_reactionClock.reset()
event.waitKeys(keyList=["space"])
v_reactionTime = v_reactionClock.getTime()
# Close Presentation Window
v_winObj.close()
# STEP 3.7: Add Bids Event Instruction for the End Screen
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"],
duration=onset_dict["onset"]+v_reactionTime,
event_role = "instruction",
word="end_text",
color="black",
pressed_key="space",
response_time=v_reactionTime,
)
events.append(bids_event)
# STEP 3.8: Add Bids Event Response for the Final Key Press
bids_event = BIDSTaskEvent(
onset=onset_dict["onset"]+v_reactionTime,
duration = "n/a", #we do not measure the length of the keypress
event_role = "response",
pressed_key="space",
response_time=v_reactionTime,
)
events.append(bids_event)
# Write the Instruction into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"],v_reactionTime,"instruction","end_text","black","space","n/a",v_reactionTime,"n/a","n/a"))
# Write the Response into the csv File
v_logFileObj.write("%f;%s;%s;%s;%s;%s;%s;%s;%s;%s\n"
%(onset_dict["onset"]+v_reactionTime,"n/a","response","n/a","n/a","space","n/a",v_reactionTime,"n/a","n/a"))
#%% STEP 4: Use events_template.json
# Set path to an existing sidecar JSON file (if one exists)
existing_file = "events_template.json"
#%% STEP 5: Save the Event File and Close
bids_example.addEvent(events)
bids_example.writeEvents(participant_info, execute_sidecar=existing_file)
# You can write a requirements.txt
bids_example.addEnvironment()
core.quit()
|
Once the experiment has run, the folder structure and event file should resemble the following:
stroop/
├── stroop.py
├── events_template.json
├── data/
│ └── 1_1.csv
└── StroopBids/
│ └── sub-1/
│ └── ses-1/
│ └── beh/
│ └── sub-1_ses-1_task-stroopbids_run-001_events.tsv
├── participants.tsv
├── participants.json
├── dataset_description.json
├── task-stroopbids_events.json
├── README
├── LICENSE
├── CHANGES
├── requirements.txt
└── .bidsignore
| onset |
duration |
event_role |
word |
color |
pressed_key |
trial_type |
response_time |
trial_number |
response_accuracy |
| 15.2876 |
1.2295 |
instruction |
instruction_text |
black |
space |
n/a |
1.2295 |
n/a |
n/a |
| 16.5171 |
n/a |
response |
n/a |
n/a |
space |
n/a |
1.2295 |
n/a |
n/a |
| 16.538 |
0.5 |
fixation |
+ |
black |
n/a |
n/a |
n/a |
n/a |
n/a |
| 17.0593 |
0.8105 |
stimulus |
Red |
green |
left |
incongruent |
0.8105 |
1.0 |
correct |
| 17.8698 |
n/a |
response |
Red |
green |
left |
incongruent |
0.8105 |
1.0 |
correct |
| 17.8972 |
1.0 |
feedback |
Correct! |
green |
n/a |
incongruent |
n/a |
1.0 |
n/a |
| 18.9272 |
0.5 |
fixation |
+ |
black |
n/a |
n/a |
n/a |
n/a |
n/a |
The plain Python base experiment file used in this tutorial is available here: stroop_basecode.py.