Interactives
Per-object regular stream • Files:
InteractivesData_<ObjectName>.csv(one per interactive) • Writer:InteractiveDataCapture.cs
Interactives doesn't cleanly fit the regular-vs-irregular framing used for the other streams. It's listed under Irregular here because each interactive's file only exists when that interactive exists and is running — files come and go rather than being a single session-wide stream. But while running, the writer emits one row per LateUpdate, so within any given file the cadence is regular.
If you only read one part of this page, read When it writes.
At a glance
Interactives captures per-interactive-object state — one file per running interactive (named InteractivesData_<ObjectName>.csv), with one row per LateUpdate while the interactive's isRunning flag is true. Each row contains the shared prefix plus runtime object state (position, rotation, scale, active/visible/solid flags, held-object reference) and the object's base, rigidbody, and physic-material settings.
Use Interactives for:
- Per-object pose/interaction timelines.
- "What happened to object X during this epoch?" analyses.
- Held-object / grab / release sequences.
For event-level interactive events (EventPoint fires from interactives, action triggers), use Events — those are logged there as part of the unified event log.
When it writes
LateUpdate, per interactive instance, while both of the following are true:
interactive.isRunning(the interactive is active in the current epoch), AND- The shared
InteractivesDataCaptureconfig hasenabledLogging = true.
When either is false, no row is emitted. Note: the InteractivesDataCapture config is auto-created on experience open (see Configuration), so by default new experiences just work — you only need to touch it to disable or override settings.
This is regular per-frame cadence per interactive, but the set of files in a run depends on which interactives existed and ran. A run with ten interactives produces up to ten InteractivesData_*.csv files.
File & location
- Pattern:
InteractivesData_<ObjectName>.csvper interactive, in the run directory. - If
InteractivesDataCapture.fileNameis overridden to e.g."MyInteractives.csv", per-interactive files becomeMyInteractives_<ObjectName>.csv. - Writer:
InteractiveDataCapture.csinRuntime/UnityComponents/EpochObjects/Interactives/. - Observation:
Runtime/DataCapture/Observations/InteractiveObservation.cs. - State model:
Runtime/DataCapture/DataModels/InteractiveState.cs.
Configuration
Shared InteractivesDataCapture SO (subclass of DataCaptureConfig) on the experience controls all per-interactive writers:
| Field | What it does |
|---|---|
enabledLogging | Master enable/disable for all interactive capture. |
fileName | Base filename; each interactive's file appends _<ObjectName>. Default InteractivesData.csv (so files become InteractivesData_<ObjectName>.csv). |
flushEveryNRows | Batched flush cadence. Default 30. |
writeHeader | Write the header row on first emission. Default true. |
Auto-created on experience open. WindowUtility.OpenExperience() populates this slot if missing. Per-object overrides live on the per-interactive InteractiveDataCaptureSettings (an ExperienceSetting), separate from this shared SO.
Columns
The header combines five column sources in order:
LaboExperienceState.GetColumns()— the shared prefix.InteractiveRunTimeUtility.GetColumns()— runtime interactive state (pos / rot / scale / flags / held).interactive.goSettings.objectBase.GetColumns()— base object settings (tag, layer, etc.).interactive.goSettings.rigidBody.GetColumns()— rigidbody settings (mass, drag, etc.).interactive.goSettings.physicMaterial.GetColumns()— physic material settings.
Exact column names are defined in those utilities — for the authoritative list, read the header line of a real CSV output.
Runtime interactive state (partial)
From InteractiveState:
| Column | Type | Description |
|---|---|---|
tag | string | Unity tag of the object. |
| position (X / Y / Z) | float | World-space position of the object (metres). |
| rotation (X / Y / Z) | float | Rotation — likely Euler angles (check header). |
| scale (X / Y / Z) | float | Local scale. |
active | bool | Unity activeSelf. |
visible | bool | Renderer enabled. |
solid | bool | Collider enabled. |
| held-object reference / id | string | The interactive currently held by this one, if any. NaN when nothing held. |
Sample rows
...shared...,tag,posX,posY,posZ,rotX,rotY,rotZ,scaleX,scaleY,scaleZ,active,visible,solid,heldObjectId,...objectBase...,...rigidBody...,...physicMaterial...
...,Stimulus,0.5,1.2,-1.5,0.0,90.0,0.0,1.0,1.0,1.0,True,True,True,NaN,...
...,Stimulus,0.55,1.2,-1.48,0.0,95.0,0.0,1.0,1.0,1.0,True,True,True,ParticipantHand,...
(Trailing columns truncated for readability — a real row has all five column groups.)
Join with other streams
Each interactive's file joins to ExperienceState on FrameNumber, same as any regular stream:
import pandas as pd
exp = pd.read_csv("ExperienceState.csv")
stim = pd.read_csv("InteractivesData_Stimulus.csv")
joined = exp.merge(stim, on="FrameNumber", how="left", suffixes=("_exp", "_stim"))
# Track Stimulus position over the Trial epoch:
trial_track = joined[joined["EpochName"] == "Trial"][["FrameNumber", "posX", "posY", "posZ"]]
To compare two interactives, join each to ExperienceState independently then merge, or join them directly on FrameNumber:
stim = pd.read_csv("InteractivesData_Stimulus.csv")
target = pd.read_csv("InteractivesData_Target.csv")
both = stim.merge(target, on="FrameNumber", how="inner", suffixes=("_stim", "_target"))
Gotchas
- One file per interactive. File set depends on which interactives ran. Don't assume a fixed list; glob
InteractivesData_*.csvand parse the<ObjectName>portion of each filename. - Files only exist when the interactive ran. If an interactive was defined but never reached
isRunning == trueduring the session, its file won't exist. - Per-interactive headers may differ. Different interactives may have different object/rigidbody/physic-material configurations, so their column sets can diverge. Read each header dynamically.
heldObjectIdis a string, not a foreign key. It's usually the object name, but"NaN"literal is used for "nothing held". Handle theNaN-as-string case explicitly — it's not a numericNaN.- Event-level interactive behaviour goes to Events, not here. If you want "when was the stimulus triggered" as an event, look in
EventPoints.csvwithEvent_Source = Interactive. - Regular cadence inside each file, but the per-interactive framing is irregular across the session. Analyses that assume a single unified stream don't apply — aggregate per-file, or merge multiple files through
FrameNumberjoins first.
Analysis recipes
- Stimulus-locked averaging — stimulus onset events from Events, stimulus pose from
InteractivesData_Stimulus.csv. - Reaction time — pair an interactive-fired event with a subsequent input.