Session Replay

To view the tracking data after a simulation is run, click "Replay" in the Dashboard details page or start the Session Replay file (also referred to as "SightLab Replay"). You will be presented with a list of options for session playback. Note: You must first run an experiment session for the session replay to show any data.
For 360 video/photo playback, the viewpoint will be from a fixed position, but can look around if uncheck "Follow Mode" (see Replaying 360 Sessions)
Spacebarto start/stop.Rto reset.- Use the slider along the bottom to scrub through the playback
Session playback can be viewed in either Desktop Mode or in a headset if you choose "Meta," "SteamVR," or "OpenXR."



Note: Session Replay uses a "Free Camera" mode, meaning you can use the WASD and ZX keys to move the camera around, or use the VR controls or walk freely if viewing in a headset. To follow the participant instead, use Follow Mode or Third Person (see Views below).
Features and Options
Trial Number
- Select any trial from your session to replay.
**Client ID **(Multi- User Only)
- Show Visualizations for only the selected client
Open Data Folder
- Opens the folder containing data files associated with the session.
Avatar Visualization
- Toggle the display of the avatar (or avatars in multi-user mode). Head and hands are shown separately.
Gaze Visualizations
- Toggle:
- Gaze Ray: The line of sight from the user (individual or per eye).
- Gaze Point: The intersection of gaze and objects (individual or per eye).
- Dwell Spheres: A sphere related to objects of interest that will change size and color based on the time spent gazing at them, once the dwell time threshold is met.
Fixation Spheres
- Toggle visualization of fixation spheres. Fixations are calculated based on:
- Angular distance (<1 degree).
- Duration (>100 ms, default; configurable).
- Click Static to view all fixations at once.
- First: Shows the time and location of the first fixation
- All: Will show a numbered sequence of all the fixations
Heat Map Visualization
- Provides a dynamic representation of gaze data:
- Dynamic Mode: Displays gaze over time during playback.
- Static Mode: Aggregates all gaze data into a single heatmap.
- Occlusion Setting: Enables heatmaps that respect physical barriers (e.g., walls).
- Note: requires setting fastHeatmap = False and can affect frame rate.
- Gradient: A heatmap that fades over a specified time.
- Adjust parameters using sliders for intensity, decay rate, and transparency.
- Note: If running a trial longer than 2 or 3 minutes you can switch to the faster heatmap if loading times increase or frame rate drops (replay = Replay.SightLabReplay(fastHeatmap = True) )
Scan Path
- Visualize gaze paths dynamically or statically.
- Options:
- Gaze Path: Tracks gaze movement.
- Dispersion Path: Tracks areas of focus.
Walk Path
- The walk path will show arrows in the direction that a user walked and will also show velocity based on the spacing between arrows. Walk Path can be shown as static or dynamic.
Console
- Toggle text overlays for additional session information such as Pupil Diameter and real time views
- ROIs: Shows the regions of interest for 360 media. Moving regions follow the paths they had during the trial (see 360 Moving Regions of Interest).
- Tags: Shows the names of the ROIs
Views
- Follow Mode: The camera moves with the participant's head, so you see the session from their point of view (first person). Uncheck it to return to the free camera.
- For 360 media: Toggle whether the head position is locked or free.
- Third Person: The camera follows the participant from just behind and above their head, so you can see their avatar head along with what they were looking at. Checking it also turns on Follow Mode, and unchecking it returns to the first-person view.
- The camera turns with the direction the participant faces but ignores head tilt, so the view stays level.
- Hold the right mouse button and drag to orbit the camera around the participant's head (left/right to circle around, up/down to swing below or above). When you let go, the camera eases back behind them.
- The avatar head is shown if Head is checked under Avatar.
- Available for 3D model scenes only, and turns off when you change trials.
- To change where the camera sits, see Third Person Camera Offset.
- For a third-person view during the experiment itself, see Third Person.

Reset to Default
- Resets all settings to their default values.

Controls
If using a headset the default controls apply, except in the case of the Vive Pro/2/Vive Pro Eye, where the system button (one above trackpad) on the right needs to be held down and use the left trackpad to move, and the left system button is used for the arc teleport. For the other headsets, the navigation is their default
| Action | Key |
|---|---|
| Start/Stop Playback | Spacebar |
| Reset Playback | R |
| Scrub Playback | C, V (or use slider) |
| Frame-by-Frame | B, N |
| Move Camera | WASD, Arrow Keys |
| Move Up/Down | X, Z |
| Rotate Camera | Q, E, O, L, or Mouse |
| Orbit Camera (Third Person) | Hold right mouse button and drag |
| Hide Menu | ., F1 |
| Take Screenshot | / |
| Video Capture (uncompressed) | Ctrl + F12 |
| Full Screen Toggle | F2 |
| Rendering Mode Toggle | F3 |
| Performance Data Toggle | F4 |
| Show Instructions | i |
| Connect to Biopac | 1 |
| Connect to Biopac Graph | 2 |
| Save Equirectangular Image | 3 |
| Reset Viewpoint | 4 |
| Exit | Escape |
Advanced Features
Heatmap Aggregation
See where people looked across many recordings of the same scene, whether from different participants or from one participant over several trials and sessions. The Group Heatmap Replay example merges every recording of a scene in a data folder into one session that plays back like a multi-user session, with each recording as its own client and one heatmap that adds up all the gaze. It works for 3D environments and 360 media.

To use it
- Download the example and extract it anywhere, for example into your
ExampleScriptsfolder. - Run
merge_sessions.pyand select the data folder of the project you recorded with: the folder that holds the<date>_experiment_datafolders (for exampleMyProject\data). Every scene recorded at least twice becomes one trial of the group session. - Run
group_heatmap_replay.pyand pick the session ending in_GROUP.
Group sessions use the same controls as the regular Session Replay:
- The Heat Map settings (Static, Intensity, Radius, Rate) apply to the combined heatmap. Static shows the whole trial at once.
- Choose a client under Client ID to show only that recording's gaze and heatmap. Choose All to see them combined again.
- Take a screenshot with
/or save an equirectangular image with3.
Scan paths, walk paths, fixation spheres, dwell spheres and the console are turned off when a group trial loads, because drawing them for many recordings slows playback. Turn them back on with their checkboxes.
Settings (in group_heatmap_config.py)
| Setting | What it does |
|---|---|
SOURCE_DATA_FOLDER |
Data folder to merge, so merge_sessions.py doesn't ask each time |
GROUP_NAME |
Name shown after the date in the session list, such as PilotA (no underscores) |
TRIAL_NUMBERS |
Merge only these trials, such as [1, 3] |
TRIAL_LABELS |
Merge only trials with these labels (conditions), such as ['A'] |
MIN_RECORDINGS |
How many recordings a scene needs to become a group trial (default 2) |
MIN_DURATION |
Skip recordings shorter than this many seconds (default 1.0) |
HEATMAP_NORMALIZATION |
'mean' shows average dwell per recording, so groups of different sizes can be compared directly. 'sum' shows total dwell across all recordings |
HIDE_PARTICIPANT_OVERLAYS |
Set to False to keep scan paths and the other per-client overlays on |
Notes
- Each merge creates a new session, named by date and time, in the example's
datafolder, so earlier merges are kept. Thegroup_manifest.csvfile in each session lists which recording each client came from. Delete a session's folder when you no longer need it. - Your original data files are only read, never changed.
- Recordings are lined up from the start of each trial on a shared 60 Hz timeline, so each recording counts equally whatever headset it was made with. The longest recording drives the scene (environment, moving objects and 360 video), so this suits studies where everyone saw the same content.
- Merged sessions point to your project's environment and media files. If you move or rename the project, run the merge again.
- Loading takes longer with more recordings (around 20 seconds for 20 recordings).
- Normal sessions also play in
group_heatmap_replay.py, exactly as in the Session Replay.
Noise Filter
Smooth eye tracking data by averaging the gaze points over a specified window of time. This helps to reduce the impact of random noise or fluctuations in the eye tracking data, resulting in more stable and reliable gaze points. The moving average algorithm works by maintaining a rolling window of the most recent gaze points and calculating the average for each new point as it becomes available.
replay = Replay.SightLabReplay(noiseFilter=True)
Synchronizing Traditional Video
For synchronizing 2D video playback with the replay (note: first need to define and add video in replay as well):
def reset():
video.setTime(0)
video.play()
def play():
video.play()
def pause():
video.pause()
def scrub(time):
video.setTime(time)
viz.callback(REPLAY_RESET, reset)
viz.callback(REPLAY_PLAYING, play)
viz.callback(REPLAY_PAUSED, pause)
viz.callback(REPLAY_SCRUB, scrub)
Custom Replay Flags and Events
Set custom flags during an experiment session and visualize them in the replay (for instance to keep track of the state of an object like color change and play that back in the replay at the correct time). See also ExampleScripts\Replay_Events
Example:
# During Experiment with GUI
def changeColor():
color_list = [viz.RED, viz.BLUE, viz.GREEN, viz.YELLOW]
target_color = random.choice(color_list)
basketball.color(target_color)
vizact.onkeydown('a', changeColor)
def getColor():
sightlab.setCustomReplayFlag('color', basketball.getColor())
#run sightlab experiment
def sightLabExperiment():
while True:
yield viztask.waitKeyDown(' ')
yield sightlab.startTrial()
#Add update here for GUI
vizact.onupdate(0,getColor)
yield viztask.waitKeyDown(' ')
yield sightlab.endTrial()
# During Replay
def changeColor():
basketball = replay.sceneObjects[GAZE_OBJECTS]['basketball']
flag_data = replay.getCurrFlag()
if "color" in flag_data:
print(flag_data)
basketball.color(flag_data["color"])
import vizact
vizact.onupdate(0,changeColor)
#Visibility
def changeVisibility():
Les_Paul.visible(viz.TOGGLE)
vizact.onkeydown("a", changeVisibility)
def getVisibility():
visibilityFlag = Les_Paul.getVisible()
sightlab.setCustomReplayFlag("visible", visibilityFlag)
vizact.onupdate(0, getVisibility)
#Replay Side
def applyReplayFlag():
Les_Paul = replay.sceneObjects[GAZE_OBJECTS]["Les_Paul"]
flag_data = replay.getCurrFlag()
if "visible" in flag_data:
print(flag_data)
Les_Paul.visible(flag_data["visible"])
vizact.onupdate(0, applyReplayFlag)
Data Directory
If you have a custom location for your data files give the path to your custom data folder:
replay = Replay.SightLabReplay(dataDirectory='data')
Advanced Configuration: Scan Path Settings
Modify the appearance of scan paths:
replay.replayVisualisations.scanPathConfig = {
POINT_COLOR: viz.GREEN,
POINT_SIZE: 2,
LINE_COLOR: viz.BLUE,
LINE_WIDTH: 2
}
Environment Access
Access the environment object during replay:
replay.transportNode.setPosition([x, y, z])
env = replay.getEnvironmentObject()
Scene Objects
Access specific scene objects during replay:
screen = replay.sceneObjects[GAZE_OBJECTS]["screen"]
Setting Starting Position of Viewpoint
replay.transportNode.setPosition([x, y, z])
Third Person Camera Offset
Set where the Third Person camera sits relative to the participant's head. offset is [x, y, z] in meters, measured in the direction the participant faces (+y is up, -z is behind them). pitch is how many degrees the camera tilts down toward them. The default is [0, 0.5, -1.5] with a pitch of 15. Leave out either argument to keep its current value. If Third Person is already on, the change applies immediately.
replay = Replay.SightLabReplay()
replay.setThirdPersonOffset([0, 1.5, -4], pitch=20)
When the participant is in a vehicle, a larger offset keeps the whole vehicle in view. The Driving example uses:
replay.setThirdPersonOffset([0.42, 0.97, -3.54], pitch=15)
To start every trial in Third Person, turn it on each time a trial loads:
def startInThirdPerson():
replay.replayGUI.thirdPersonON()
viz.callback(TRIAL_LOADED_EVENT, startInThirdPerson)
startInThirdPerson() # the first trial has already loaded
Loading Older Replay Files
Steps for replaying data from older SightLab versions (1.x):
-
Copy the blank project folder.
-
Move older data files to a folder named
data. -
Place environments or media in the appropriate
Resourcessubfolders. -
Use this code to enable compatibility:
replay = Replay.SightLabReplay(sightlab_1_9=True)
Trial Loaded Event
Perform an action when trials are changed
def ontrialchanged():
trial_number = int(replay.currTrial)
print("change: ", trial_number)
viz.callback(TRIAL_LOADED_EVENT, ontrialchanged)