Experiment Configuration#
The information about the experimental setup in which the eye-tracking data was collected plays a crucial role in its processing. Screen size, viewing distance, coordinate origin, and sampling rate all determine how pixel
positions translate into physical or visual units. In pymovements, this contextual information is stored in an Experiment object, which describes the geometry and timing of the recording setup.
Providing an experiment is not strictly required to load gaze data: samples can still be inspected in raw pixel coordinates and timestamps without it (see Inspecting Raw Samples with Plots). However, the experiment definition becomes essential as soon as gaze samples are interpreted in physical or visual terms, for instance, converting pixels to degrees of visual angle or computing velocities (see Transforming Raw Samples). These conversions are necessary for detecting oculomotor events such as fixations and saccades (see Detecting Occumotoric Events).
The Experiment object has two main components: Screen and an EyeTracker. These can be first defined explicitly and then combined to form an Experiment.
Screen#
The Screen class describes the physical and pixel properties of the display used during recording. These parameters determine how gaze positions in pixels map to real-world or visual-angle units. They include:
screen resolution in pixels (
width_px,height_px)physical screen size in centimeters (
width_cm,height_cm)eye-to-screen distance (
distance_cm)coordinate origin (e.g.,
"upper left"or"center")
import pymovements as pm
from pymovements import Experiment
screen = pm.Screen(
width_px=1920,
height_px=1080,
width_cm=53.0,
height_cm=30.0,
distance_cm=65.0,
origin="upper left",
)
screen
-
distance_cm:
65.0
-
height_cm:
30.0
-
height_px:
1080
-
origin:
'upper left'
-
tuple (2 items)
- 1920
- 1080
-
tuple (2 items)
- 53.0
- 30.0
-
width_cm:
53.0
-
width_px:
1920
-
x_max_dva:
22.16991132451976
-
x_min_dva:
-22.16991132451976
-
y_max_dva:
12.982992572251874
-
y_min_dva:
-12.982992572251874
If all physical parameters (width_cm, height_cm, distance_cm) and pixel resolution are provided, screen coordinates can be converted from pixels to degrees of visual angle (dva, °). The screen boundaries in dva are available via the properties x_min_dva, x_max_dva, y_min_dva, and y_max_dva.
screen.x_max_dva
22.16991132451976
Variable Viewing Distance#
In many laboratory setups, the eye-to-screen distance is constant and can be specified once via distance_cm in the experiment definition. However, in some recordings, for example in head-free or mobile eye-tracking setups, the distance between the eye and the screen may vary over time. In such cases, the viewing distance can be provided per sample as a column in the Gaze data instead of as a fixed value in the experiment. The eye-to-screen distance is required for converting pixel coordinates into degrees of visual angle (dva).
Coordinate Origin#
The origin parameter defines where pixel coordinate (0, 0) is located on the screen. This choice affects how gaze positions are interpreted and transformed. The origin should match the coordinate system used during stimulus presentation.
EyeTracker#
The EyeTracker class stores temporal and device-specific properties of the eye tracker model. It contains:
sampling rate in Hz
which eye(s) were recorded
optional metadata about the device
The sampling rate is especially important for time-based computations such as velocity estimation and event detection.
eyetracker = pm.EyeTracker(
sampling_rate=1000.0,
model="EyeLink 1000 Plus",
left=False,
right=True,
version="2.0",
vendor="EyeLink",
mount="Arm Mount / Monocular / Remote",
)
eyetracker
-
left:
False
-
model:
'EyeLink 1000 Plus'
-
mount:
'Arm Mount / Monocular / Remote'
-
right:
True
-
sampling_rate:
1000.0
-
vendor:
'EyeLink'
-
version:
'2.0'
Sampling Rate#
The sampling rate determines the temporal resolution of the recording and is stored in the eye tracker component of the experiment. Typically reported in hertz (Hz), it specifies how many gaze samples are recorded per second. Values range from around 30 Hz for low-cost or webcam-based systems to 500 Hz or 2000 Hz for high-end research-grade eye trackers.
In general, higher sampling rates allow finer temporal detail to be captured. The Nyquist–Shannon sampling theorem states that a signal must be sampled at least twice as fast as its highest frequency component to avoid aliasing. In practice, this means that high sampling rates are required to capture rapid eye movements such as saccades, while lower sampling rates may be sufficient for analyses focused on fixations or overall viewing patterns.
The Experiment Object#
Now the initialized Screen and EyeTracker objects can be passed directly to the Experiment constructor using the screen and eyetracker parameters.
experiment = pm.Experiment(screen=screen, eyetracker=eyetracker)
experiment
-
EyeTrackerEyeTracker
-
left:
False
-
model:
'EyeLink 1000 Plus'
-
mount:
'Arm Mount / Monocular / Remote'
-
right:
True
-
sampling_rate:
1000.0
-
vendor:
'EyeLink'
-
version:
'2.0'
-
left:
-
ScreenScreen
-
distance_cm:
65.0
-
height_cm:
30.0
-
height_px:
1080
-
origin:
'upper left'
-
tuple (2 items)
- 1920
- 1080
-
tuple (2 items)
- 53.0
- 30.0
-
width_cm:
53.0
-
width_px:
1920
-
x_max_dva:
22.16991132451976
-
x_min_dva:
-22.16991132451976
-
y_max_dva:
12.982992572251874
-
y_min_dva:
-12.982992572251874
-
distance_cm:
Alternatively, an Experiment can be created directly by passing the required screen and eye tracker parameters to the constructor. In this case, the corresponding Screen and EyeTracker objects are initialized internally.
experiment_flat = Experiment(
screen_width_px=1280,
screen_height_px=1024,
screen_width_cm=38.0,
screen_height_cm=30.0,
distance_cm=68.0,
origin="upper left",
sampling_rate=1000.0,
)
experiment_flat
-
EyeTrackerEyeTracker
-
left:
None
-
model:
None
-
mount:
None
-
right:
None
-
sampling_rate:
1000.0
-
vendor:
None
-
version:
None
-
left:
-
ScreenScreen
-
distance_cm:
68.0
-
height_cm:
30.0
-
height_px:
1024
-
origin:
'upper left'
-
tuple (2 items)
- 1280
- 1024
-
tuple (2 items)
- 38.0
- 30.0
-
width_cm:
38.0
-
width_px:
1280
-
x_max_dva:
15.599386487782953
-
x_min_dva:
-15.599386487782953
-
y_max_dva:
12.4277916428235
-
y_min_dva:
-12.4277916428235
-
distance_cm:
All discussed parameters can be accessed directly as attributes of the Experiment instance. For example, the sampling rate can be accessed and printed as follows:
experiment.sampling_rate
1000.0
Loading Experiment from a Dictionary#
Experiments can also be constructed from dictionaries, which is useful when reading metadata from configuration files.
experiment_from_dict = Experiment.from_dict({
"screen": {
"width_px": 1280,
"height_px": 1024,
"width_cm": 38.0,
"height_cm": 30.0,
"distance_cm": 68.0,
"origin": "upper left",
},
"eyetracker": {
"sampling_rate": 1000.0,
}
})
experiment_from_dict
-
EyeTrackerEyeTracker
-
left:
None
-
model:
None
-
mount:
None
-
right:
None
-
sampling_rate:
1000.0
-
vendor:
None
-
version:
None
-
left:
-
ScreenScreen
-
distance_cm:
68.0
-
height_cm:
30.0
-
height_px:
1024
-
origin:
'upper left'
-
tuple (2 items)
- 1280
- 1024
-
tuple (2 items)
- 38.0
- 30.0
-
width_cm:
38.0
-
width_px:
1280
-
x_max_dva:
15.599386487782953
-
x_min_dva:
-15.599386487782953
-
y_max_dva:
12.4277916428235
-
y_min_dva:
-12.4277916428235
-
distance_cm:
experiment.to_dict()
{'eyetracker': {'sampling_rate': 1000.0,
'left': False,
'right': True,
'model': 'EyeLink 1000 Plus',
'version': '2.0',
'vendor': 'EyeLink',
'mount': 'Arm Mount / Monocular / Remote'},
'screen': {'width_px': 1920,
'height_px': 1080,
'width_cm': 53.0,
'height_cm': 30.0,
'distance_cm': 65.0,
'origin': 'upper left'}}
Saving Experiment Settings#
Experiment configurations can be exported to a dictionary or YAML file for
reproducibility.
experiment.to_yaml("experiment.yaml")
This makes it easy to store the recording setup alongside your data and reuse it in later analyses.
In the next section, we will use this experiment definition while loading gaze data and explore how raw samples are represented inside pymovements.