v0.23.0
Added
- Stimulus-font install hint — when a dataset declares its typeface
(MultiplEYE), a note under Text font names the font, links a download, and
gives per-OS install steps. The overlaid labels only match the stimulus image
pixel-for-pixel when that exact font is installed (the browser otherwise falls
back per script, so a CJK font's half-width Latin — URLs/digits — drifts); the
note also points to the stimulus image as the reliable fallback. - Corpus Analysis — question-oriented analysis sections (AN-1…28) — the single
Aggregated Views subtab is replaced by four sections, each answering one
question, plus the WIP Generations tab:- Per text (one text, many readers): stacked per-reader word profiles with an
optional cohort-mean overlay, a word × reader heatmap, the cohort word profile
with a spread band, word difficulty tinted on the true-to-scale stimulus, a
per-word measure-vs-linguistic-feature scatter (surprisal / frequency / length /
POS) with a trend line, and skip / regression-in rates per word. - Per reader (one reader, many trials): measure distribution vs the cohort
(violin/box), a reading-speed summary card with cohort percentiles, fixation
duration over time, the saccade-amplitude × fixation-duration density scatter,
progressive vs regressive saccades per trial, the landing-position (PVL) curve,
and this reader's per-trial trend. - Per group (a cohort): pooled distributions, the group word profile, a
per-reader summary table, and the group trend (optionally with per-reader lines). - Group comparison (two cohorts): overlaid distributions, the per-word A−B
difference profile (diverging colormap + zero line), paired summary bars with
error bars, an effect size + significance test (Cohen's d, Welch t-test /
Mann–Whitney; exploratory), and a stacked two-group word heatmap. - Cross-cutting controls every section reads: a shared measure picker
(TFD/FFD/FPRT/RPD/n_fixations/skip/regression-in/out + fixation duration /
saccade amplitude), mean/median/sum aggregation and SD/SEM/IQR/bootstrap-CI
spread, a z-score within reader normalization toggle, a min-readers/min-trials
guard, and a per-view tidy-table CSV download. Groups are defined either by
splitting a categorical field or by two independent filter sets. The per-text
stimulus view now honours the activeglobal_*visualization settings instead
of hard-coded display options (AN-28). Addsscipyfor the group tests.
- Per text (one text, many readers): stacked per-reader word profiles with an
- Saccade line width — a width slider (0.5–10 px, default 2) in the Saccade-style
popover, threaded through every plot, bulk export, save/restore, and deep links
(per-scanpath in comparisons); plus headless--saccade-color/--saccade-style/
--saccade-widthCLI flags and aplot_scanpath(saccade_width=…)kwarg. - Show full monitor toggle — frame the plot to the whole presentation monitor
instead of cropping to the data extent (single, animated, comparison, bulk export). - MultiplEYE corpus support —
load_multipleye(), the in-app Public datasets
picker, and the Add dataset wizard (browser upload) load the multilingual
reading corpus (identity parsed from folder/file names, one trial per stimulus
page, character AOIs aggregated to word boxes), with its comprehension questions,
pre-aggregated reading measures (IA_*), reader metadata, and a true-to-scale
stimulus-page background image. - OneStop public dataset — the Public datasets picker now offers the
OneStop 360-participant
English corpus, downloading the paragraph-level interest-area + fixation
reports from OSF on demand (regime-selectable:
ordinary / information seeking / repeated / information-seeking-repeated),
rendered true-to-scale on OneStop's 2560×1440 monitor. Distinct from the
env-var$ONESTOP_DATA_DIRserver bundle. See docs. - Public datasets (PoTeC + MultiplEYE) shown by default (
SCANPATH_PUBLIC_DATASETS=0
to hide). Schema auto-detection now recognizes MultiplEYE columns. - MultiplEYE server-bundle source — a URL-addressable
multipleyedata
source (?source=multipleye) for deep-linking a review app straight into the
viewer at a given participant + trial. Gated on$MULTIPLEYE_DATA_DIRpointing
at a raw export root, it reuses the native MultiplEYE loader (same raw frames,
schema auto-detection, and authoritative 1920×1080 monitor) as the public
source — so it renders identically — and fast-paths to a single session for
?participant(resolved case-insensitively). Exposed on every surface: the
sidebar source list, the Share/deep-link round-trip, and headless
render --source multipleye [--export DIR]. Mirrors the OneStop server
bundle. - Upload-wizard helpers — derive trial/participant ids from the filename
(delimiter split or regex) and aggregate character AOIs into word boxes. - Fixation classification (visual only) — under the Fixation controls, flag
short / long / out-of-bounds fixations and either highlight them (chosen
marker + colour) or discard them from the plot, with editable short/long
duration thresholds. Affects only what's drawn — measures and exports are
unchanged. Replaces the old out-of-text marker toggle. - Drag-to-reorder trial chips — an inline ✏️ Edit chips popover beside the
chip strip (replaces the sidebar picker): drag fields between Shown and
Available and reorder within Shown. The chip strip's More dropdown now
holds the full chip list (so any chip clipped at the line edge is always
reachable) alongside the summary stats. - Column mapping surfaces auto-detected columns per field (and flags when
you've overridden the detected default). - Fixation marker opacity — an opacity slider (global + per-scanpath, default
0.7) replaces the Hollow circles toggle, so overlapping fixations show through;
threaded through every plot, animation, comparison, bulk export, save/restore and
deep links. - Fixation-index window on the main plot — a range slider in the Fixation popover
draws only fixations (and their saccades) within the chosenorder_in_trialrange,
applied to the single, animated and comparison views; chips/panels keep the full trial.
Changed
- Public-datasets picker reworked (DATA-4…7) — the corpus list is now a
searchable selectbox (shows each corpus' short name + a language · size
caption, one-line description, and home link) that scales as more datasets are
added; local/private upload stays the primary path. Each loader now shows
an Expected files layout for its data directory and a found vs. Download
status — a one-click ⬇ Download (PoTeC, OneStop) replaces the always-on
Download if missing checkbox, so an already-downloaded corpus never re-checks
the network. The per-source participant/text narrowing (PoTeC Texts/Readers,
MultiplEYE Sessions/Stimuli) is gone — each source loads the whole corpus and
the global Narrow by trial filters scope it. (The headlessload_potec/
load_multipleyekeep theirreaders/texts/sessions/stimuliargs.) - Internal: split
app.py(4087 → ~1640 lines) into focused modules —
deep-link/share/config →url_state.py, the upload wizard →wizard.py— and
factored plot overlay layers into helpers. No behavior change. - Navigation streamlined — Corpus Analysis is a header toggle; Data Inspection
and Share are Scanpath subtabs; the sidebar view-nav is gone. - Trial selection reworked into Filter → Pick — Text/Participant/condition
filters narrow the pool, then one row picks the trial (dropdown + scrubbing
slider + ◀ ▶); Browse-by modes removed. - Comparison styling moved into the per-layer Fixation/Saccade popovers, beside
the single-trial controls. - Compare mode reworked. The second-trial selector sits above the chips and
mirrors the main picker — trial id + ★ same-text / 👤 same-participant
markers in the dropdown (ordered stars → same-participant → rest),index/N · id
slider, ◀ ▶. The overlay/layout + show-A/B-legend config moved into a rail
⚙️ Compare popover; the A/B legend (static and animated overlay) is
optional and hidden by default; the figure title is removed; each chip strip's
trial id is coloured to its scanpath (replacing the A/B legend line). Color
fixations by now works in compare too — it colours both scanpaths by the metric
(shared scale), with the per-scanpath flat colour as the A/B marker outline; the
redundant global saccade controls stay hidden. - Welcome tour walks the whole Scanpath screen in reading order (plot →
selection → chips → rail → panels → sidebar); the redundant Exit button is
gone (the ✕ closes it). - Styling & chrome polish — saccade arrows off by default, span-border colour,
horizontal/rotatable colour bars, tighter control rail, less heading clutter,
a dismiss ✕ on the welcome tour.
Fixed
- MultiplEYE reading text now aligns with the stimulus image (BUG-3). The
loader reads the realFONT_SIZE+ font from the stimulus config and stamps
them onto the data (stimulus_font_px/stimulus_font_family); on a dataset
switch the app snaps its font controls to the exact size and CJK typeface (and
off "scale text to boxes", since the precise px is known), so the word labels
land on the printed text instead of being inferred ~3× too small in a generic
font. The scale-text-to-boxes path also improved: the font is budgeted from
the line pitch (not the glyph-tight box height) and the box-width cap is
script-aware (full-width CJK vs half-width Latin, per word), so CJK corpora and
mixed CJK+Latin lines size sensibly. OneStop sizing is unchanged. - Compare-mode fixations no longer turn black. The per-scanpath colour pickers
(rendered only when Compare is on) desynced to black and committed that on the
next interaction; they now pass an explicit value and a falsy colour can't leak. - Trial filter resets on a dataset switch (and is restored per-dataset when you
switch back), so a stale filter can't silently apply to a different corpus. - Clearer image-export errors when Chrome is missing — point to
kaleido_get_chrome/ the browser-free HTML export, with a pre-flight check and
a warm→cold render fallback for the animation export. - Stimulus image now shows in Animate mode (was static-only).
- Switching public datasets re-detects the column mapping and snaps the canvas to
that corpus' monitor — fixes MultiplEYE pages collapsing into one trial and
off-scale rendering. - Heatmap colour matches the picker (default now blue; style uses a radio).
- Save & restore captures every setting — colour-bar orientation/ticks,
fixation classification, span-border colour, and per-scanpath comparison styling. - Chips stay on one line when space is tight.
Install/upgrade: pip install -U scanpath-studio