mousewheel is not completely smooth but still better than nothing

This commit is contained in:
2026-08-17 19:15:13 +01:00
parent 0e6f95495e
commit 103161eb6b
+50 -9
View File
@@ -16,6 +16,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/8.1.2/adapter.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { overflow: hidden; }
body {
background: #000;
display: flex;
@@ -653,16 +654,36 @@
}
}, 3000);
let lastMouseMove = 0;
// The actual network send happens in a requestAnimationFrame loop
// below instead of a manual setInterval-style throttle — RAF aligns
// with the browser's own natural scheduling/render cadence, which
// browsers are heavily optimized to prioritize correctly even under
// other event traffic (like rapid wheel scrolling)
let accumDx = 0, accumDy = 0;
let hasPendingMove = false;
document.addEventListener('mousemove', e => {
if (!pointerLocked || !isControlling) return;
const now = performance.now();
if (now - lastMouseMove < 16) return;
lastMouseMove = now;
sendInput({ type: 'mouse_move', dx: e.movementX * mouseSensitivity, dy: e.movementY * mouseSensitivity });
pingActive();
accumDx += e.movementX;
accumDy += e.movementY;
hasPendingMove = true;
});
function rafMoveFlush() {
if (hasPendingMove) {
sendInput({ type: 'mouse_move', dx: accumDx * mouseSensitivity, dy: accumDy * mouseSensitivity });
accumDx = 0;
accumDy = 0;
hasPendingMove = false;
// No pingActive() here — it sends an actual WebSocket message every
// call, and this flush can run up to ~60x/sec during movement,
// doubling real network traffic during exactly the situation we're
// trying to lighten. The existing 3-second setInterval already
// keeps the idle timer alive without needing this.
}
requestAnimationFrame(rafMoveFlush);
}
requestAnimationFrame(rafMoveFlush);
canvas.addEventListener('contextmenu', e => {
e.preventDefault();
});
@@ -680,12 +701,32 @@
let lastMouseWheel = 0;
canvas.addEventListener('wheel', e => {
if (!pointerLocked || !isControlling) return;
e.preventDefault();
// No e.preventDefault() here anymore, and this listener is passive.
// preventDefault() on a non-passive wheel listener forces the browser
// to synchronously coordinate with its compositor thread on every
// single call — at 60-140 wheel events/sec (rapid scroll-jump
// spamming) that repeated synchronous cost was confirmed via the
// diagnostic counters above to correlate exactly with mousemove
// events dropping to zero during the same window: the browser was
// coalescing/dropping mouse movement entirely while busy handling
// the wheel events. Page scroll is prevented via CSS
// (html, body { overflow: hidden }) instead, which is free and
// doesn't require blocking per-event JS work.
const now = performance.now();
if (now - lastMouseWheel < 16) return;
// 100ms instead of 16ms: jump timing has no real use for 60Hz
// precision (a single jump+land cycle takes far longer than that),
// and the diagnostic counters showed raw wheel dispatch hitting
// 70-80/sec during a fast physical flick — almost certainly the
// mouse wheel's hardware free-spinning generating many rapid encoder
// pulses from one gesture, not 70-80 deliberate jumps. This won't
// reduce how often the browser calls this handler, but it does cut
// the actual sendInput -> WebSocket -> xdotool-click work by a large
// factor, which was still contributing some interference even after
// removing preventDefault().
if (now - lastMouseWheel < 100) return;
lastMouseWheel = now;
sendInput({ type: 'mouse_wheel', direction: e.deltaY < 0 ? 'up' : 'down' });
}, { passive: false });
}, { passive: true });
const KEY_MAP = {
'KeyW': 'w', 'KeyA': 'a', 'KeyS': 's', 'KeyD': 'd',