Chrome에서 1000Hz가 가능한가? 이벤트 루프를 우회하여 마우스 폴링 레이트 테스트
Source: Dev.to

문제: 시각화 vs. 입력
대부분의 웹 애니메이션은 requestAnimationFrame(rAF)에 의존합니다. 모니터가 60 Hz라면 rAF는 약 16.6 ms마다 실행됩니다. rAF 루프 안에서 1000 Hz 마우스(1 ms마다 데이터 전송)를 측정하면 약 94 %의 데이터를 놓치게 됩니다.
입력 스레드와 렌더 스레드를 분리해야 합니다.
해결책: pointermove + performance.now()
- 화면 업데이트보다 더 자주 발생하는 원시
pointermove이벤트를 청취합니다(Chrome/Edge는 “Coalesced Events”를 제공합니다). - 고해상도 시간 API(
performance.now())를 사용해 마이크로초 단위 정밀도를 확보합니다—Date.now()는 밀리초 단위 정확도만 제공합니다.
코드
let lastEventTime = 0;
let pollingRateSamples = [];
window.addEventListener('pointermove', (event) => {
// 1. Get the microsecond‑precise timestamp
const currentEventTime = performance.now();
// 2. Calculate the delta (time between two events)
const timeDelta = currentEventTime - lastEventTime;
// 3. Convert to Frequency (Hz)
// Hz = 1000 ms / delta
const instantaneousHz = 1000 / timeDelta;
// 4. Filter out noise (idle or super slow movements)
if (instantaneousHz > 10 && instantaneousHz < 8000) {
pollingRateSamples.push(instantaneousHz);
}
lastEventTime = currentEventTime;
// NOTE: Do NOT update the DOM here!
// Updating the UI every 1 ms will kill browser performance.
// Store the data, and visualize it in a separate requestAnimationFrame loop.
});
“이벤트 루프” 함정
브라우저는 배터리를 절약하기 위해 이벤트를 그룹화합니다. pointermove 리스너 안에서 무거운 작업을 수행하면 프레임 레이트에 맞춰 스로틀링이 발생합니다.
회피 방법
- 리스너를 가볍게 유지합니다(수학 연산과 배열 푸시만).
- 시각화를 분리합니다: 별도의
requestAnimationFrame루프에서pollingRateSamples를 읽어 그래프를 그립니다.
결과
입력 캡처와 렌더링을 분리함으로써, 네이티브 소프트웨어와 일치하는 일관된 측정값을 얻을 수 있었습니다.

실제 데모를 여기서 체험해 보세요: Mouse Polling Rate Test
이 스위트에는 게임패드 테스터(스틱 드리프트용)와 데드 픽셀 테스트도 포함되어 있으며, 모두 브라우저에서 로컬로 실행됩니다.
토론
현대 웹 API가 많은 “유틸리티” 데스크톱 도구들을 대체할 만큼 강력하다는 것을 증명하기 위해 만들었습니다.
여러분은 어떻게 생각하시나요? 이제 500 MB 드라이버 스위트를 완전히 삭제할 수 있는 시점인가요, 아니면 하드웨어와의 상호작용에서는 네이티브 앱이 언제나 최고일까요?
댓글로 의견을 알려 주세요!