Skip to content

Fix mouse-wheel zoom misclassification in camera controls#926

Merged
slimbuck merged 2 commits into
playcanvas:mainfrom
slimbuck:input-dev
Jun 29, 2026
Merged

Fix mouse-wheel zoom misclassification in camera controls#926
slimbuck merged 2 commits into
playcanvas:mainfrom
slimbuck:input-dev

Conversation

@slimbuck

Copy link
Copy Markdown
Member

Summary

Replaces the stateful, burst-based device classifier in the camera controller's wheel handler with a modifier-key-only model, mirroring the approach already shipped in supersplat-viewer. The previous classifier tried to distinguish a physical mouse wheel from a trackpad two-finger swipe using per-event heuristics (wheelDelta % 120, deltaMode, fractional/diagonal deltas), but hi-res mice emit smooth deltas that are indistinguishable from a trackpad. On the reporter's Logitech G502 X (Windows 11), a bare wheel was misclassified as a trackpad ~95% of the time and fell through to the orbit branch instead of zooming. The same misfire was reported on Windows Edge.

Root cause

A bare wheel event and a bare trackpad swipe carry no reliable signal that tells them apart — every delta-based heuristic misfires on some hardware or in momentum tails. The old code's else fallback was orbit(deltaX, deltaY), so any wheel that failed the "is this a mouse?" check orbited.

Approach

The one trackpad signal the platform reliably provides is the macOS/Magic-Mouse pinch: a wheel event with ctrlKey set while the physical Ctrl key is not held (synthetic Ctrl). So we stop classifying the device entirely and drive everything from modifier keys, treating a bare scroll as zoom regardless of input device.

To tell a synthetic-Ctrl pinch from a user physically holding Ctrl while scrolling, we track the physical Ctrl key with keydown/keyup listeners on window (cleared on blur and on destroy). These are registered directly rather than through the wrap helper, so Ctrl tracking does not fire camera.controller on every keystroke.

Input mapping: old vs new

Orbit mode

Input Old New
Mouse wheel (classified as wheel) zoom zoom
Trackpad two-finger swipe (classified as trackpad) orbit zoom
Hi-res mouse wheel misclassified as trackpad orbit (bug) zoom (fixed)
Pinch (synthetic Ctrl) zoom zoom
Cmd + scroll zoom zoom
Physical Ctrl + scroll zoom orbit
Shift + scroll pan pan

Fly mode

Input Old New
Bare scroll / pinch dolly along view Z dolly along view Z
Physical Ctrl + scroll dolly along view Z look
Shift + scroll dolly along view Z pan

The Shift-remap workaround (reading deltaX when macOS maps a Shift+wheel to deltaY === 0) is preserved.

Trade-off

A bare macOS trackpad two-finger swipe now zooms instead of orbiting; trackpad orbit moves to Ctrl + swipe. This is unavoidable — a bare wheel and a bare swipe are physically indistinguishable, so fixing mouse-wheel zoom necessarily changes bare-swipe behavior. This matches the resolution already adopted in supersplat-viewer and the tension noted by the maintainer in the issue thread.

Test plan

Verified at runtime by dispatching synthetic wheel events against the live editor canvas and reading camera state before/after:

  • Bare wheel → distance changes, azim/elevation unchanged (zoom)
  • Synthetic-Ctrl pinch (ctrlKey, no physical Ctrl) → zoom
  • Physical Ctrl held + scroll → azim/elevation change (orbit)
  • Shift + scroll → focal point moves (pan)
  • Ctrl keyup resets tracking → a subsequent ctrlKey wheel is treated as pinch/zoom again
  • tsc --noEmit and eslint clean, no console errors

@slimbuck slimbuck requested a review from Copilot June 29, 2026 12:31
@slimbuck slimbuck self-assigned this Jun 29, 2026
@slimbuck slimbuck added the bug Something isn't working label Jun 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes mouse-wheel zoom being misclassified as trackpad input in the camera wheel handler by removing delta-based “device classification” heuristics and instead using a modifier-key-only model (with special handling for macOS synthetic-Ctrl pinch).

Changes:

  • Removed burst/stateful wheel-vs-trackpad classification heuristics and made bare scroll always zoom in orbit mode.
  • Added physical Ctrl tracking (keydown/keyup + reset on blur/destroy) to distinguish macOS synthetic-Ctrl pinch from real Ctrl+scroll.
  • Updated fly-mode wheel behavior to map physical Ctrl+scroll to look and Shift+scroll to pan.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/controllers.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@slimbuck slimbuck marked this pull request as ready for review June 29, 2026 13:47
@slimbuck slimbuck requested a review from a team June 29, 2026 13:47
@slimbuck slimbuck merged commit cb54b90 into playcanvas:main Jun 29, 2026
2 checks passed
@slimbuck slimbuck deleted the input-dev branch June 29, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants