Skip to content

Instantly share code, notes, and snippets.

@zalo
Last active September 30, 2024 22:52
Show Gist options
  • Save zalo/c91f61812bda9756c911473300ac1d02 to your computer and use it in GitHub Desktop.
Save zalo/c91f61812bda9756c911473300ac1d02 to your computer and use it in GitHub Desktop.
Mouse Jitter Testing
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fullscreen Canvas</title>
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
display: block;
}
</style>
</head>
<body>
<canvas id="fullscreenCanvas"></canvas>
<script>
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('fullscreenCanvas');
const context = canvas.getContext('2d');
let lastTimeMS = 0;
let time = 0.0;
let numEvents = 0;
let lastNumEvents = 0;
let rawPointerEvents = [];
let pointerEvents = [];
let jitters = [];
canvas.addEventListener('pointerrawupdate', (event) => {
rawPointerEvents.unshift(event);
});
canvas.addEventListener('pointermove', (event) => {
let timeMS = event.timeStamp;
pointerEvents.unshift(event);
let deltaTime = timeMS - lastTimeMS;
//requestAnimationFrame(animate);
context.fillStyle = 'black';
context.clearRect(0, 0, canvas.width, canvas.height);
//context.fillText('Events per second: ' + lastNumEvents, 10, 10);
let rmse = 0.0;
if(pointerEvents.length > 100) { pointerEvents.length = 100;}
if(pointerEvents.length >= 3) {
const x0 = pointerEvents[0].clientX;
const y0 = pointerEvents[0].clientY;
const x1 = pointerEvents[1].clientX;
const y1 = pointerEvents[1].clientY;
const x2 = pointerEvents[2].clientX;
const y2 = pointerEvents[2].clientY;
// Find the inverse lerp from the distance of 2 to 1 and 3
const t1to0 = Math.sqrt((x0 - x1) ** 2 + (y0 - y1) ** 2);
const t1to2 = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
const t = (t1to0 / (t1to0 + t1to2));
jitters.unshift(t);
if(jitters.length > 100) { jitters.length = 100; }
}
//context.fillText('Deviation: ' + jitters[0], 10, 20);
for(let i = 0; i < jitters.length; i++) {
context.fillStyle = 'rgb('+(Math.abs(jitters[i] - 0.5) * 255 * 10)+', 0, 0)';
context.fillRect(jitters[i] * 300.0, 50 + i * 5, 5, 5);
rmse += (jitters[i] - 0.5) ** 2.0;
}
context.fillStyle = 'black';
rmse = Math.sqrt(rmse / jitters.length);
context.fillText('Jitter RMSE: ' + Math.round( rmse * deltaTime * 200) / 100 + " ms", 10, 30);
const sortedDeviations = jitters.map((x) => Math.abs(x - 0.5)).toSorted();
const median = sortedDeviations[Math.floor(sortedDeviations.length / 2)];
context.fillText('Jitter Median: ' + Math.round( median * deltaTime * 200) / 100 + " ms", 10, 40);
context.moveTo(300.0, 50.0);
context.lineTo(300.0, 550.0);
context.stroke();
for(let i = 1; i < pointerEvents.length-1; i++) {
const x = pointerEvents[i].clientX;
const y = pointerEvents[i].clientY;
context.fillStyle = 'rgb('+(Math.abs(jitters[i-1] - 0.5) * 255 * 10)+', 0, 0)';
//context.fillStyle = 'rgb(255, 0, 0)';
context.fillRect(x - 5, y - 5, 10, 10);
}
//for(let i = 0; i < rawPointerEvents.length; i++) {
// const event = rawPointerEvents[i];
// const x = event.clientX;
// const y = event.clientY;
// context.fillStyle = 'blue';
// context.fillRect(x - 25, y - 25, 50, 50);
//}
if(!time) { time = 0; }
time += deltaTime;
numEvents = rawPointerEvents.length;
rawPointerEvents = [];
//if(rawPointerEvents.length > 100) { rawPointerEvents.length = 100; }
//console.log('Time:', time);
if (time >= 1000){
lastNumEvents = numEvents;
time -= 1000;
numEvents = 0;
}
lastTimeMS = timeMS;
});
//animate();
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
//// Example drawing
//context.fillStyle = 'blue';
//context.fillRect(0, 0, canvas.width, canvas.height);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment