fix: reduce live view scroll sensitivity#188
Conversation
Replace the old throttle-and-clamp scroll handler with a fixed-rate polling approach that sends at most 1 scroll tick per 100ms interval in each direction. This dramatically reduces scroll speed for both mouse wheel and trackpad input: - Mouse wheel: 1 notch now sends 1 tick (~120px) instead of 7-10 ticks - Trackpad: max 10 ticks/sec instead of 60-100 ticks/sec - Accumulated deltas are tracked with a jitter threshold to filter noise - Interval auto-cleans when scroll input stops Also lower the default scroll sensitivity from 10 to 3. Made-with: Cursor
The scroll slider (1–100) now controls the polling interval: - scroll=1 → 200ms interval (5 ticks/sec, slowest) - scroll=10 → 185ms interval (5.4 ticks/sec, default) - scroll=50 → 117ms interval (8.5 ticks/sec) - scroll=100 → 33ms interval (30 ticks/sec, fastest) Restore default scroll to 10 (the original value) since the setting is now meaningful again. Made-with: Cursor
Made-with: Cursor
Reintroduce magnitude-aware tick calculation so fast scrolling feels responsive. Accumulated pixel deltas are divided by 120 (one Chromium scroll-notch worth of pixels) and clamped by the scroll setting. Slow scroll (1 notch ≈ 100px) still produces 1 tick; fast scroll (500px accumulated) now produces ~4 ticks instead of always 1. Made-with: Cursor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| const scaleAxis = (delta: number): number => { | ||
| if (Math.abs(delta) < JITTER_THRESHOLD) return 0 | ||
| const ticks = delta / PIXELS_PER_TICK | ||
| const clamped = Math.min(Math.max(Math.round(ticks), -this.scroll), this.scroll) |
There was a problem hiding this comment.
Missing default scroll change undermines sensitivity fix
Medium Severity
The scaleAxis function clamps tick count to this.scroll, and the PR description states the default was lowered from 10 to 3 in settings.ts, but that companion change is missing from this commit. With the existing default of 10, scaleAxis allows up to 10 ticks per interval (~6480 px/sec max) instead of the intended 3 ticks (~1800 px/sec), largely defeating the purpose of capping trackpad scroll speed.
| this.pendingScrollX += x | ||
| this.pendingScrollY += y |
There was a problem hiding this comment.
The core issue here is that the old code clamped raw pixel deltas
(~100px per notch) directly as tick counts — that's the 10x multiplier bug. This can be fixed much more simply by dividing by 120 (pixels per Chromium
scroll notch) in the existing code path:
x = x === 0 ? 0 : Math.min(Math.max(Math.round(x / 120) || Math.sign(x), -this.scroll), this.scroll)
y = y === 0 ? 0 : Math.min(Math.max(Math.round(y / 120) || Math.sign(y), -this.scroll), this.scroll)That keeps the existing throttle and avoids the interval/accumulation/cleanup
machinery. I wonder if starting with a more minimal fix and only adding complexity based on testing would be easier for us long term ^^
There was a problem hiding this comment.
Good call — you're right that the core bug is just a missing divisor, and the interval/accumulation machinery is unnecessary complexity to solve it. Simplifying to your approach now.
Replace the interval/accumulation machinery with a one-line fix per Sayan's review: divide raw pixel deltas by 120 (one Chromium scroll notch) before clamping. Keeps the existing throttle, avoids interval lifecycle complexity. Old: 100px delta → clamped to 10 → 10 XButton events → 1200px scroll New: 100px delta → 100/120 = 1 → 1 XButton event → 120px scroll Made-with: Cursor
| // Note that if deltamode is line or page won't matter since we aren't | ||
| // sending the mouse wheel delta to the server anyway. | ||
| // The difference between pixel and line can be important however since | ||
| // we have a threshold that can be smaller than the line height. |
There was a problem hiding this comment.
this comment seems useful? and maybe should be expanded with the change in this pr
There was a problem hiding this comment.
Good catch — restored the comment and expanded it to explain the PIXELS_PER_TICK divisor, the 1:1 mapping to XTestFakeButtonEvents, and how the scroll setting clamps fast swipes.
Adds back the removed deltaMode/pixel comment and expands it to explain the PIXELS_PER_TICK divisor and scroll clamp. Made-with: Cursor


Checklist
Summary
Users (Nava, Gabe) reported that live view scrolling is too sensitive/fast. The root cause is that the old scroll handler clamped raw pixel deltas directly to the
scrollsetting (default 10), meaning a single mouse wheel notch (~100px) would send 10 XTestFakeButtonEvent clicks to Chromium — each scrolling ~120px, totaling ~1200px per notch.This PR replaces the old throttle-and-clamp approach with a fixed-rate polling scroll handler that:
Before vs After
Also lowers the default scroll sensitivity setting from 10 to 3.
Files changed
images/chromium-headful/client/src/components/video.vue— new scroll handlerimages/chromium-headful/client/src/store/settings.ts— lower default scroll valueTest plan
build-docker.shand run withrun-docker.sh(ENABLE_WEBRTC=true)Made with Cursor
Note
Medium Risk
Changes how wheel input is converted into remote scroll events; small logic errors could make scrolling feel broken or inverted on some devices/browsers, but scope is limited to the client wheel handler.
Overview
Reduces live-view scroll sensitivity by changing
video.vue’sonWheelhandler to normalize wheel deltas to pixel units (handling non-zerodeltaMode) and then convert pixel deltas into discrete scroll “ticks” (≈120px per tick) before applying the userscrollclamp.This replaces the previous direct clamp of raw deltas, aiming to prevent trackpads/fast wheel gestures from generating excessive remote scroll events while preserving invert-scroll behavior.
Written by Cursor Bugbot for commit b6eb9dd. This will update automatically on new commits. Configure here.