Skip to content

Conversation

EFCTO
Copy link
Contributor

@EFCTO EFCTO commented Sep 14, 2025

Description:

This PR optimizes the mousemove event handler in InputHandler.ts
by throttling it with requestAnimationFrame to reduce CPU usage.

No UI or gameplay logic is changed.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

GOC

@EFCTO EFCTO requested a review from a team as a code owner September 14, 2025 05:23
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

coderabbitai bot commented Sep 14, 2025

Walkthrough

MouseMoveEvent emissions are throttled to once per animation frame. The mousemove handler now stores the latest event and schedules a requestAnimationFrame if needed. The RAF callback emits a single MouseMoveEvent per frame when movement is detected. No public APIs were changed.

Changes

Cohort / File(s) Change summary
Input handling throttling
src/client/InputHandler.ts
Batch mousemove events per animation frame using pendingMouseMove and a frame flag; emit MouseMoveEvent only once per frame with clientX/clientY when movementX/movementY indicate movement. Other input logic unchanged.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Window
  participant InputHandler
  participant EventBus

  User->>Window: mousemove (many)
  Window->>InputHandler: onMouseMove(evt)
  Note over InputHandler: Save latest evt as pendingMouseMove<br/>If no frame scheduled -> requestAnimationFrame

  rect rgba(200,240,255,0.25)
  Window-->>InputHandler: RAF tick
  InputHandler->>InputHandler: Take and clear pendingMouseMove
  alt movementX or movementY present
    InputHandler->>EventBus: emit MouseMoveEvent(clientX, clientY)
  else
    Note over InputHandler: Skip emit (no movement)
  end
  end

  Note over EventBus,InputHandler: At most one MouseMoveEvent per frame
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

Feature - Frontend, UI/UX

Suggested reviewers

  • scottanderson

Poem

One mouse to move, many signals to tame,
Frames keep time in a gentle game.
Events now dance in tidy queues,
One per beat—no frantic news.
Smooth as paint on a canvas bright,
The cursor glides, the frames feel right.

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "perf(InputHandler): optimize mousemove handling to reduce CPU usage" clearly and concisely summarizes the main change (throttling mousemove with requestAnimationFrame) and uses a conventional commit-style scope, so it communicates the primary intent to teammates scanning history.
Description Check ✅ Passed The PR description directly describes the change (throttling mousemove with requestAnimationFrame), notes no UI/gameplay logic changes, and includes the author's manual testing confirmation, so it is related to the changeset and passes this lenient check.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/client/InputHandler.ts (2)

196-199: movementX/movementY can be 0 or undefined on some Safari/trackpad paths — add coord fallback

Relying only on movementX/Y may drop real moves on Safari/iPadOS or very slow motion. Guard with clientX/Y deltas too.

Apply:

@@
-          if (evt && (evt.movementX || evt.movementY)) {
-            this.eventBus.emit(new MouseMoveEvent(evt.clientX, evt.clientY));
-          }
+          if (
+            evt &&
+            (
+              evt.movementX || evt.movementY ||
+              evt.clientX !== lastMouseX || evt.clientY !== lastMouseY
+            )
+          ) {
+            lastMouseX = evt.clientX;
+            lastMouseY = evt.clientY;
+            this.eventBus.emit(new MouseMoveEvent(evt.clientX, evt.clientY));
+          }

Plus, define these near the other locals:

@@
-    let pendingMouseMove: MouseEvent | null = null;
-    let mouseMoveFrame = false;
+    let pendingMouseMove: MouseEvent | null = null;
+    let mouseMoveFrame = false;
+    let lastMouseX = 0;
+    let lastMouseY = 0;

186-187: Make the handler and rAF cancelable; avoid leaks on re‑init/hot‑reload

The inline listener cannot be removed in destroy(); the scheduled rAF cannot be canceled either. Store both on the instance and clean up in destroy().

Example (outline):

// fields
private mouseMoveRAF: number | null = null;
private mouseMoveHandler?: (e: MouseEvent) => void;

// in initialize()
this.mouseMoveHandler = (e) => { /* same logic, but use this.mouseMoveRAF instead of a boolean */ };
window.addEventListener("mousemove", this.mouseMoveHandler);

// in destroy()
if (this.mouseMoveHandler) window.removeEventListener("mousemove", this.mouseMoveHandler);
if (this.mouseMoveRAF != null) cancelAnimationFrame(this.mouseMoveRAF);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 835c8d9 and 698d2aa.

📒 Files selected for processing (1)
  • src/client/InputHandler.ts (1 hunks)
🔇 Additional comments (1)
src/client/InputHandler.ts (1)

189-195: rAF throttle looks good

Batches to one emit per frame; this should cut CPU spikes from hot mousemove paths.

Please sanity‑check:

  • 30 fps caps (VSync off / battery saver) to catch perceived lag.
  • Pointer lock mode (if used anywhere).

@evanpelle
Copy link
Collaborator

Do you have a perf profile to show how much cpu is saved?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants