Gallery¶
Every figure on this page is drawn from the bundled demo while the docs are built, by the code shown under it, so what you see is what the current release renders. Hover a fixation or a word for its values. The app draws the same figures from its plot controls, and its Share subtab prints the code that reproduces whichever one is on screen.
All of them start from the demo and one of its trials:
import scanpath_studio as sps
words, fixations = sps.load_sample_data()
pid, tid = "l37_1129", "l37_1129_2_1_1_Ele_r0"
A reading¶
The app's Scanpath design, which plot_scanpath draws by default: each
fixation where it landed, sized by its duration, and the saccades between
them, over the text at its recorded position. The orange words are the answer
to the trial's question.
Where the participant dwelt¶
The heatmap on its own, here as a smooth duration-weighted density rather than one tint per word; the app's Heatmap controls offer both.
fig = sps.plot_scanpath(
words,
fixations,
pid,
tid,
show_heatmap=True,
heatmap_style="Interpolated",
show_fixations=False,
show_saccades=False,
)
Fixations by text line¶
Each fixation colored by the line of text it was assigned to: a quick check for vertical drift, which shows up as one line's color creeping onto the next.
Saccades by reading class¶
Each saccade colored by its role in reading: forward, skip, refixation, return sweep, or regression.
ForwardSkipRefixationReturn sweepRegressionOther
fig = sps.plot_scanpath(
words,
fixations,
pid,
tid,
saccade_color_mode="By type",
saccade_type_legend=False,
)
A linear-reading schematic¶
One sentence's fixations snapped above the words they landed on, with the saccades arced over the text. It no longer shows exact positions, so the figure labels itself an Illustration.
fig = sps.plot_scanpath(
words,
fixations,
pid,
tid,
fixation_snap_to_word=True,
saccade_render_mode="Arc",
fix_index_range=(124, 139),
)
Two participants, one text¶
The first fifty fixations of two trials of the same paragraph, on one canvas in two colors. The trials can also sit side by side, or come from two different datasets.
fig = sps.compare_scanpaths(
words,
fixations,
(pid, tid),
("l7_1090", "l7_1090_2_1_1_Ele_r0"),
fix_index_range=(1, 50),
show_legend=True,
)
Raw gaze under the fixations¶
Gaze samples, colored by time, under the fixations, which are drawn hollow so the samples show through. The demo ships no recorded samples, so this trial's are synthesized from its fixations: the figure shows the layer, not real data.
raw_gaze = sps.load_sample_raw_gaze()
fig = sps.plot_scanpath(
words,
fixations,
"l37_1129",
"l37_1129_2_2_2_Adv_r0",
raw_gaze=raw_gaze,
hollow_fixations=True,
show_saccades=False,
)
The replay¶
The reading unfolding fixation by fixation, here its first forty. Press ▶; the app replays in real time or faster, and exports the replay as HTML, GIF or MP4.
A text, word by word¶
Beyond single trials: the total fixation duration on every word of one text, averaged over the demo's participants, with ± one standard deviation as a band. The app's Corpus Analysis view draws this and more.
# The demo's word table carries EyeLink's own measures (IA_DWELL_TIME, …).
one_text = words[words["unique_paragraph_id"] == "2_1_1_Ele"]
profile = (
one_text.groupby("IA_ID")
.agg(
value=("IA_DWELL_TIME", "mean"),
sd=("IA_DWELL_TIME", "std"),
word_text=("IA_LABEL", "first"),
)
.rename_axis("word_id") # the column plot_corpus_figure reads
.reset_index()
.assign(lo=lambda d: d.value - d.sd, hi=lambda d: d.value + d.sd)
)
fig = sps.plot_corpus_figure(
profile, kind="profile", measure_label="Total fixation duration (ms)"
)