Skip to main content

Data and exports

This guide explains how a participation's data is organized once you export it, so you can analyze it with confidence. For the list of files in an export bundle and how to download them, see Participation details. This page focuses on the concepts behind those files: the time basis, the shape of each kind of record, and how to join them.

Time basis

Every timestamp in an export is expressed in recording time: milliseconds counted from the camera's first frame. This is the same scale as the playhead on the participation page, so a value of 12000 is twelve seconds into the recording.

Each export also reports a synchronization quality so you know how reliable that alignment is. See Timing and data quality for what the precise, approximate, and unavailable levels mean and how to handle each. When quality is unavailable, recording-time columns are left blank rather than filled with an unreliable value.

Absolute wall-clock time

Recording time tells you where an event sits inside one recording. To place that recording on the real-world calendar (to line it up with an external device, or to compare two participations), the export's metadata.json carries a precise_times block with absolute, server-anchored timestamps.

These timestamps are anchored to a single reading of the server clock taken near consent, not to the participant's local machine clock. That makes them distinct from the legacy session_started_at and session_ended_at fields, which come straight from the local machine clock and carry no error bound. Do not mix the two: precise_times is server-anchored and comes with a stated uncertainty, while the session_started_at family is a local-clock reading. A timing column that reads close to another is not a reason to assume they share a clock.

precise_times holds three events:

  • participation_start: the instant the participation page opened.
  • record_time_0: the camera's first-frame instant, the same moment that recording time 0 refers to. This event is derived at export time (it needs the recording's start offset), so it appears only here in metadata.json, not on the participation document.
  • participation_end: the instant the participation ended.

Each event is an object with these fields:

FieldMeaning
epoch_msthe absolute time as a UTC timestamp in milliseconds
isothe same instant as an ISO-8601 UTC string (ends with Z)
error_msthe worst-case uncertainty in milliseconds, or null when no bound applies
boundedtrue when the time carries a trustworthy error bound, false otherwise
clock_sourceserver_offset when anchored to the server clock reading, or local_fallback when that reading was unavailable
recording_sync_sourceon record_time_0 only: chirp_anchored for sub-millisecond audio alignment, or approximate_sync when the recording start could not be pinned

The error_ms value is calibration uncertainty: it reflects the round-trip of the one server-clock reading taken near consent, measured once. It is not a measure of how much the clock drifted over the session. When the clock is trustworthy, a participation_start reads as bounded: true with a small error_ms; if the device clock jumps or sleeps during the session, the times are reported as bounded: false with error_ms set to null, since the single calibration can no longer be trusted.

clock_source (how the wall clock was anchored) and recording_sync_source (how the recording start was aligned) are two independent quality signals. A session can have a solid clock anchor but only an approximate recording start, or the reverse.

Reconstructing wall-clock time for any event

Because record_time_0 is the absolute time of recording time 0, you can turn any recording-time value into an absolute time with one addition:

wall_clock_ms = record_time_0.epoch_ms + recording_ms

Here recording_ms is any recording-time value from the export (a marker, a response, a signal sample), and recording_ms = 0 is the camera's first frame. When record_time_0 is reported as bounded: false (for example on an approximate_sync session), treat every wall-clock time you reconstruct from it as approximate as well.

Response records and grain

Each behavioral task writes one response file per task type (for example responses/csv/flanker.csv and a lossless responses/json/flanker.json). How rows map to the task depends on the task's grain:

GrainOne record perExamples
trialtrialStroop, Flanker, N-back
eventdiscrete event (no trial number)trail making, some eye-tracking tasks
singlewhole task (one record)questionnaire, drawing
sequenceordered step, with a final answertoken test, social ladder

Grain also determines how trials are counted in study_summary.json: trial-grain tasks report real trial counts, while event-grain and single-grain tasks leave those counts blank because the concept does not apply. The grain for each task is noted in the export's README.md.

The flat CSV holds one row of scalar fields for spreadsheet use; the JSON keeps the full record, including any nested fields the CSV omits. Fields whose name ends in a time suffix are converted to recording time on export; the data_dictionary.csv and codebook.json describe every field.

Summary files

Tasks that produce aggregate statistics also write a summary file, one per task (for example summaries/flanker_0.json). Every summary follows a common versioned envelope:

  • schema_version: the envelope version (currently 1), so your analysis code can detect shape changes between platform releases.
  • task: an identity block that names which task this summary belongs to. It includes the task kind, the task version, the run index, and the join keys (task_id, display_order, task_name) that link this file to the other export files. Use task.task_id to join a summary to the corresponding response rows or signal samples.
  • measures: a flat map of headline computed values for the whole task: trial counts, mean reaction time, accuracy, and any task-specific derived scores such as interference effects or signal-detection indices. These are the same quantities shown in the "Computed measures" section of the Scoring tab.
  • breakdowns (optional): the same measures split by a factor such as condition, block, or category. Structure: breakdowns.<factor>.<level>.<measure>.
  • meta (optional): task-specific context that is not itself a measure, such as the mode the task ran in, the word list presented, or whether the task was aborted early.
  • practice (optional): a parallel block containing measures and breakdowns for the practice phase, present when the task included a practice phase.

Per-trial rows are not duplicated in the summary. All trial-level data lives in the response files (responses/csv/<kind>.csv and responses/json/<kind>.json); the summary is built from those rows and reports only the computed aggregates. The exact measures reported for each task are documented on the individual task page.

Markers and events

markers.csv lists every system and task marker on the recording-time line. Alongside each marker, a category describes the kind of event it records, so you can filter or group without memorizing every marker name:

CategoryRecords
lifecyclesession and task start/stop points
instructioninstruction and hint screens
phaseblock boundaries (practice phase, main phase, and similar)
trial onsetthe start and end of each trial
stimulusstimulus, fixation, cue, and target events
feedbackfeedback shown to the participant
per itemper-item interactions (a touch, a stroke, a click)
task specificevents unique to one task

Markers you add yourself while reviewing a participation are exported separately in researcher_markers.csv, with a note of whether each one was typed manually or copied from the system timeline.

Researcher scores

The custom metrics you record on the Scoring tab of the participation page are exported in scores/researcher_scores.csv, with a scores/researcher_scores.json companion that groups the same rows by task. The files appear only when you have entered at least one custom metric.

Each row holds one metric for one task: the task it belongs to, the metric name, the value, and your notes. The value and notes are free-form text you authored, not computed numbers, so treat them as strings: a value may be a number, a label, a rating, or a short phrase, exactly as you typed it. Convert to numbers yourself when your analysis needs them.

Join researcher scores to the rest of the export by task, the same way you join the other files (see Putting it together). Because the metric names are your own, keep them consistent across participations so the same metric lines up when you combine sessions.

Signal streams

When the relevant data was collected, an export includes continuous signal streams:

  • signals/gaze.csv: one row per gaze sample (viewport position as a percentage).
  • signals/interaction.csv: pointer, scroll, keyboard, and focus events.

Both streams are tagged at export time with the trial and phase each sample fell in (trial_index, practice_trial_index, is_practice), so you can line up gaze or interaction with the trial that was on screen. A sample carries an incomplete_trial flag when the trial it belongs to had no explicit end marker and its window was closed at the next trial's onset; treat those windows as approximate.

The field catalogue

Two files describe the data so you do not have to infer field meanings:

  • data_dictionary.csv: a flat, six-column dictionary (name, type, meaning, unit, values, task) covering every column in the export.
  • codebook.json: the same catalogue in machine-readable form, suitable for loading into an analysis script.

Both are generated from the platform's task definitions, so they always match the data in the bundle they ship with.

Putting it together

Use study_summary.json as the index of what the participant actually ran, in order. From there, join the other files by the task_id key (and, within a task, by trial number). The README.md included in every export spells out the join recipe for that specific bundle.