How your cursor reveals what you're really thinking about ranked results.
Pick a layout. Pick a question. Browse the answers like a search engine — read what interests you, skip what doesn't. The library captures how your cursor approaches, dwells on, and retreats from each result.
Press d on any SERP page to toggle this debug overlay.
Every finalized approach-retreat episode becomes a PostHog ar_episode event with the full 19-field flatten below, plus a 10% downsampled cursor trajectory (xytvxvy/1 format — flat integer array, time relative to entry).
Four-class taxonomy splits the non-click outcomes into deferred (retreated but re-approached), evaluated_rejected (retreated and never returned), and not_approached (visible but cursor never entered the AOI).
A session summary fires on visibilitychange/pagehide with per-position classification counts and time-to-first-click. Append ?ph=0 to any URL to disable capture entirely.
Everything we log on every result you hover. Nothing hidden, nothing invented.
position | rank in the SERP, from data-position |
|---|---|
outcome | four-class: clicked | deferred | evaluated_rejected | not_approached |
visited | always true for emitted episodes |
clicked | whether the visit ended in a click |
retreated | whether the cursor exited the AOI without clicking |
visit_number | 1 = first visit, 2+ = re-approach |
dwell_ms | total time the cursor was over the AOI |
|---|---|
entered_at | when cursor crossed into the AOI (performance.now) |
exited_at | when cursor left the AOI |
clicked_at | populated only when the visit ended in a click |
approach_velocity | px/ms at the moment of entry — fast = scanning, slow = evaluating |
|---|---|
approach_angle | atan2(vy, vx) at entry, radians |
peak_velocity | max speed while over the result |
min_velocity | min speed while over — near-pause = reading |
retreat_distance | px from AOI center at max retreat (0 if clicked) |
sample_count | number of raw mousemove samples captured |
direction | forward = at/near scroll high-water mark; regressive = scrolled back up |
|---|---|
entry_scroll | window.scrollY at entry |
hwm_at_entry | running max of scrollY at entry |
ar_session_id (UUID), ar_layout, ar_query_id, ar_year_min/ar_year_max,
viewport w/h/dpr, ar_ua, ar_referrer, ar_page_path, ar_loaded_at.
ar_trajectory
Flat integer array in xytvxvy/1 format: [x, y, t_rel_ms, vx, vy, x, y, ...].
10% downsample (every 10th sample) from the native ~60Hz mousemove stream.
A typical 5-second episode ships ~700 bytes of trajectory. Research-grade material, kept so cursor reanalysis stays possible.
Schema defined in src/approach-retreat.js Episode.toJSON()
· adapter in src/adapters/posthog.js.
Every variant uses the same library. Different geometries, same AOI contract.
Content swaps via ?q= on any layout page.
All 20 layout × question combinations. Linkable, bookmarkable, shareable.