| subject | trial | time | x | y | task_correct | hoo_id | hoo_pos | color | space |
|---|---|---|---|---|---|---|---|---|---|
| STU172607h07554f66 | 4_NSNC_L | 0.7999999971 | 864 | 595 | 1 | mul | L | N | N |
| STU172607h07554f66 | 4_NSNC_L | 53.7999999974 | 864 | 595 | 1 | mul | L | N | N |
| STU172607h07554f66 | 4_NSNC_L | 68.0999999940 | 864 | 595 | 1 | mul | L | N | N |
| STU172607h07554f66 | 4_NSNC_L | 91.0000000003 | 850 | 612 | 1 | mul | L | N | N |
| STU172607h07554f66 | 4_NSNC_L | 129.8999999913 | 850 | 612 | 1 | mul | L | N | N |
| STU172607h07554f66 | 4_NSNC_L | 144.2000000034 | 850 | 612 | 1 | mul | L | N | N |
Using webgazeR with PsychoPy
PsychoPy is another platform webgazeR can read webcam eye-tracking data from via merge_webcam_files(kind = "psychopy"). This vignette walks through what a PsychoPy export looks like, how to merge it, and a real timing issue worth knowing about.
Packages
options(stringsAsFactors = FALSE)
options("scipen" = 100, "digits" = 10)
library(webgazeR)
library(dplyr)
library(knitr)What PsychoPy exports
Unlike Gorilla, jsPsych, or Labvanced, PsychoPy has no fixed export schema – a PsychoPy experiment’s CSV is just whatever columns the experiment’s own code logged, one row per trial. That means there’s no standard column name for subject or trial id to infer automatically; you tell merge_webcam_files which columns to use via col_map. What does matter for merging:
- Some participant id column (here,
pid) - Some trial id column – PsychoPy doesn’t require one to be named
trial_indexlike jsPsych does, so use whatever your experiment logged as long as it’s unique per row (here,Problem_id, a label like"4_NSNC_L") array_col: the column holding per-trial gaze samples, as unkeyed triplets[[t, x, y], ...]rather than jsPsych’s keyed{x, y, t}objects – soarray_key = FALSE
Merging
psychopy_file <- "data-raw/psychopy/STU172607h07554f66.csv"
edat_pp <- merge_webcam_files(
file_paths = psychopy_file,
kind = "psychopy",
col_map = list(subject = "pid", trial = "Problem_id"),
array_col = "TaskGazeArray",
array_key = FALSE
)webgazeR ships this example already merged as psychopy_eyedata, so the rest of this vignette uses that directly rather than needing the raw CSV on disk:
edat_pp <- webgazeR::psychopy_eyedataThe result is the same standardized subject/trial/time/x/y long format as the other platforms, plus whatever other trial-level fields the experiment recorded:
dim(edat_pp)[1] 13006 19
n_distinct(edat_pp$trial)[1] 144
What’s happening under the hood
time is converted from seconds to milliseconds
This is the one thing worth knowing about PsychoPy specifically. PsychoPy’s clocks report elapsed time in seconds – confirmed directly in this file’s raw TaskGazeArray, where a trial’s timestamps run from 0.0008 to 5.04. Every other kind in this package (Gorilla, jsPsych, Labvanced) reports time in milliseconds, and so does the rest of the pipeline (e.g. binning functions that expect ms). Left unconverted, that mismatch doesn’t error – it just silently inflates any time-based statistic downstream by ~1000x.
merge_webcam_files now always multiplies PsychoPy’s time by 1000, so it’s consistent with every other platform:
range(edat_pp$time)[1] 0.300000012 12925.799999997
Next steps
From here, everything in the main webgazeR vignette applies the same way it would to Gorilla, Labvanced, or jsPsych data – AOI assignment (assign_aoi()), downsampling/upsampling (downsample_gaze(), upsample_gaze()), smoothing and interpolation (smooth_gaze(), interpolate_gaze()), and plotting (plot_IA_proportions()) are all platform-agnostic once your data is in this standardized subject/trial/time/x/y form. As with Labvanced and jsPsych, x/y here are raw pixel coordinates, not Gorilla’s normalized 0-1 coordinates – this experiment’s win_width/win_height columns are what you’d use to normalize them or define AOI boundaries in the same units.