Downloading data
Three tabs — questionnaire answers, participant info, custom logs — exportable as CSV, JSON, or ZIP.
These pages are not yet fully reviewed. The LUIDA team is continuing to review and improve them. If you find anything wrong on these pages, or have questions that aren't resolved by reading them, please ask or report to the LUIDA team.
The data your experiment captures — questionnaire answers, participant environment info, SendDataToCollector payloads — lands on the experiment detail page's Data section. This page is where the four data streams come together, and where you pull them out as CSV, JSON, or ZIP for analysis.
You won't see any data here until at least one session has run against the experiment (with isTestMode=true test sessions counting as well).
Screenshot pending — the Data section of an experiment detail page, with the three tabs visible (Questionnaire Answers, Participant Info, Custom Logs), showing the Custom Logs tab with five rows of sample data and the format dropdown set to CSV.
The three tabs
The Data section is organized into three sub-tabs, one per data stream. The fourth stream (between-subjects assignments) is folded into the Participant Info tab rather than getting its own.
1. Questionnaire Answers
One row per (session, participant, question) tuple. Columns:
| Column | Source | Notes |
|---|---|---|
experimentId | Auto | The eID — same for every row. |
questionnaireId | Auto | The qID (1-based). |
participantsIdfc | Auto | The participant's Cluster IDFC. Same participant across sessions has the same value. |
participantsRole | Auto | The participant's role for paired sessions (participant_1, participant_2). |
questionTitle | From the questionnaire | The question text. |
answer | Participant submission | An array — single value for Radio/Toggle/Text/Linear Scale, multiple values for Checkboxes. |
answerAt | Auto | UTC timestamp of submission. |
There is no sessionId column on questionnaire answer records. To join answers back to a session for between-subjects analysis, you'll have to match on (participantsIdfc, answerAt) against the Participant Info records. This is acknowledged as a limitation; see the Web Console roadmap.
A filter row at the top lets you narrow by qID, by participant role, and by time range before exporting.
2. Participant Info
One row per (session, participant) pair. Records environment info and the between-subjects assignment for that session. Columns include:
| Column | Source | Notes |
|---|---|---|
sessionId | Auto | The sID. Multiple participants in one session share this. |
participantsIdfc | Auto | The participant's Cluster IDFC. |
participantsRole | Auto | participant_1, participant_2, etc. |
platform | From the join request | VR, WINDOWS, MACOS, ANDROID, IOS. |
device | From the join request | Headset / device model (where Cluster provides it). |
os | From the join request | Operating system version. |
betweenSubjectsConditions | ConditionManager | A JSON object keyed by variable name, containing the between-subjects values assigned at session start. |
joinedAt | Auto | UTC timestamp of join. |
leftAt | Auto, nullable | UTC timestamp of disconnect, if applicable. |
This is the canonical record of "who ran what condition," which is what you'd join your custom-data table against for between-subjects analyses.
3. Custom Logs
One row per ProcessAndSaveCollectedData() invocation in your scene. The columns are whatever your per-scene data calculator script (Assets/_Experiment_/Scripts/DataCollectors/<SceneName>.js) returns from its calculateData() function.
For a Stroop tutorial setup, a typical row might look like:
| Column | Value (example) |
|---|---|
sID | 64e2f9... |
pID | 1 |
trialID | 7 |
depth | near |
fontColor | red |
textMeaning | BLUE |
responseTarget | color |
correct | false |
rt_ms | 812 |
The shape of each row is entirely up to you — it's whatever object your calculateData() returns. See Reference → Variables → COLLECTED_DATA for the patterns and gotchas of writing that function.
The filter row at the top lets you narrow by session ID, participant role, and time range.
Export formats
Each tab has a Format dropdown and a Download button. Three formats are supported:
| Format | When to use |
|---|---|
| CSV | Statistical software (R, JASP, SPSS, Python pandas, MATLAB). Default. |
| JSON | Custom analytics pipelines, scripts that need the original nested structures (e.g., the answer array on questionnaire records). |
| ZIP | Bundle of both CSV and JSON in one download — useful for archival. |
The download contains only the current filter's matching rows. To pull everything, clear the filters before clicking Download.
Custom-log columns can include nested objects or arrays if your calculateData() returns them. CSV will flatten via JSON.stringify (creating a single string column); JSON preserves the structure. For nested data, JSON is usually the right choice.
What about between-subjects assignments?
Between-subjects assignments are part of the Participant Info tab — every row's betweenSubjectsConditions column carries the JSON object of variable values that were assigned for that session. There's no separate "assignments" download.
If you only want the assignments without the rest of the Participant Info, download the Participant Info tab as JSON and project to {sessionId, betweenSubjectsConditions} in your analysis script.
Storage and retention
The Web Console stores all four streams in MongoDB:
- Questionnaire answers, custom logs, participant info: indexed by
eIDand timestamped. - Retention is currently indefinite — LUIDA doesn't auto-delete old data. (The LUIDA team will give 30 days' notice if retention policy changes.)
- Deleted experiments take their data with them — confirm the data is exported before you delete an experiment row.
Common pitfalls
- No
sessionIdon questionnaire answers. The most common analysis surprise. Join viaparticipantsIdfcand timestamp; see the Concepts page note. - CSV flattening nested objects unexpectedly. Use JSON if your custom-log rows have arrays or nested objects.
- Filtering by
pIDand finding nothing. UseparticipantsRole(participant_1, etc.), notpIDdirectly — the column name is the Cluster convention, not LUIDA's 1-based index. - Exporting an empty data set without realizing it. Check the row count at the top of each tab before downloading. If it's
0, no sessions have produced data of that type yet. - Test-mode data mixed with production. Test sessions (
isTestMode=true) are recorded with the same schema as production sessions. Use the session ID exclusion lists or filter onjoinedAtto separate them.
Where to go next
- Concepts → Data collection — the full lifecycle of a custom-data record from
SendDataToCollectorto here. - Reference → Variables →
COLLECTED_DATA— write the data calculator that defines the custom-log columns. - Concepts → Conditions, sessions, participants — how
sID,pID, andIDFCrelate. - Sessions & balancing — exclude sessions whose data you don't want to count.
Uploading avatars
Upload VRM files to your experiment's avatar set so AssignAvatarToParticipant can use them. Includes special avatar names, thumbnail generation, and the relationship to the project-level AvatarRegistry.
Settings & account
Profile info, language toggle, and the danger zone — what each setting does and what happens when you delete an account.