<h1>Pointer Lock - 🐭 Acceleration Sample</h1>
<button id="firstRequestPointerLockButton">
  request pointer lock
</button>
<button id="secondRequestPointerLockButton" autofocus>
  request pointer lock with "unadjustedMovement"
</button>
<button id="clearButton">
  clear
</button>
<pre id="mouseLog">movementX: -     max: -     min: -    </pre>
<pre id="unadjustedMouseLog">movementX: -     max: -     min: -    </pre>
<hr />
<div id="log"></div>
<canvas id="canvas"></canvas>
html {
  height: 100%;
}

body {
  font-family: helvetica, arial, sans-serif;
  margin: 2em;
}

h2 {
  margin-top: 2em;
  text-align: right;
}

button {
  margin-bottom: 0.5em;
}

#log,
pre {
  margin: 1em 0 0 0;
  white-space: pre-wrap;
  word-break: break-all;
  text-shadow: 1px 1px 0 white, -1px -1px 0 white, 2px 2px 0 white,
    -2px -2px 0 white;
}

#unadjustedMouseLog {
  margin: 0 0 1em 0;
}

#unadjustedMouseLog::after {
  content: " (without acceleration)";
  white-space: nowrap;
}

#mouseLog::after {
  content: " (with acceleration, by default)";
  white-space: nowrap;
}

canvas {
  z-index: -1;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

@media screen and (min-width: 640px) {
  body {
    margin: 2em auto;
    max-width: calc(640px - 2em);
  }
}
console.log = (text) => {
  log.textContent += `${text}\r\n`;
};

const myTargetElement = document.body;
let isLockedWithUnadjustedMovement = false;

// Request pointer lock.

firstRequestPointerLockButton.onclick = requestPointerLockWithoutUnadjustedMovement;

function requestPointerLockWithoutUnadjustedMovement() {
  const promise = myTargetElement.requestPointerLock({
    unadjustedMovement: false
  });

  isLockedWithUnadjustedMovement = false;
  if (!promise) return;

  return promise
    .then(() => console.log("pointer is locked without unadjusted movement"))
    .catch((error) => {
      console.log(
        `pointer can't be locked without unadjusted movement: "${error.message}"`
      );
    });
}

document.addEventListener("pointerlockchange", () => {
  if (document.pointerLockElement) {
    console.log(`pointer is locked on ${document.pointerLockElement.id}`);
  } else {
    console.log("pointer is unlocked");
  }
});

document.addEventListener("pointerlockerror", () => {
  console.log("pointer lock error");
});

// Disable mouse acceleration.

secondRequestPointerLockButton.onclick = requestPointerLockWithUnadjustedMovement;

function requestPointerLockWithUnadjustedMovement() {
  const promise = myTargetElement.requestPointerLock({
    unadjustedMovement: true
  });

  if (!promise) {
    isLockedWithUnadjustedMovement = false;
    console.log("disabling mouse acceleration not supported");
    return;
  }

  return promise
    .then(() => {
      isLockedWithUnadjustedMovement = true;
      console.log("pointer is locked with unadjusted movement");
    })
    .catch((error) => {
      console.log(
        `pointer can't be locked with unadjusted movement: "${error.message}"`
      );
      if (error.name === "NotSupportedError") {
        // Some platforms may not support unadjusted movement.
        // You can request again a regular pointer lock.
        return requestPointerLockWithoutUnadjustedMovement();
      }
    });
}

// Clear data.

clearButton.onclick = (_) => {
  maxMovementX = 0;
  maxUnadjustedMovementX = 0;
  minMovementX = 0;
  minUnadjustedMovementX = 0;
  mouseLog.textContent = "movementX: -     max: -     min: -    ";
  unadjustedMouseLog.textContent = "movementX: -     max: -     min: -    ";
  mouseMoveData = [];
};

// Mouse move events handling.

let maxMovementX = 0;
let maxUnadjustedMovementX = 0;
let minMovementX = 0;
let minUnadjustedMovementX = 0;

document.onmousemove = (event) => {
  const unadjusted =
    document.pointerLockElement && isLockedWithUnadjustedMovement;
  if (!unadjusted) {
    maxMovementX = Math.max(event.movementX, maxMovementX);
    minMovementX = Math.min(event.movementX, minMovementX);
    mouseLog.textContent = `movementX: ${event.movementX
      .toString()
      .padEnd(5)} max: ${maxMovementX
      .toString()
      .padEnd(5)} min: ${minMovementX.toString().padEnd(5)}`;
  } else {
    maxUnadjustedMovementX = Math.max(event.movementX, maxUnadjustedMovementX);
    minUnadjustedMovementX = Math.min(event.movementX, minUnadjustedMovementX);
    unadjustedMouseLog.textContent = `movementX: ${event.movementX
      .toString()
      .padEnd(5)} max: ${maxUnadjustedMovementX
      .toString()
      .padEnd(5)} min: ${minUnadjustedMovementX.toString().padEnd(5)}`;
  }
  mouseMoveData.push({ x: event.movementX, unadjusted });
};

let mouseMoveData = [];

(function draw() {
  canvas.width = innerWidth * devicePixelRatio;
  canvas.height = innerHeight * devicePixelRatio;
  mouseMoveData
    .filter((mouseMove, index) => mouseMoveData.length - index < canvas.width)
    .forEach((mouseMove, index) => {
      const x = index;
      const y = canvas.height;
      const w = 1;
      const h = -Math.abs(mouseMove.x) * devicePixelRatio;
      const context = canvas.getContext("2d");
      context.fillStyle = mouseMove.unadjusted ? "#ccc" : "#888";
      context.fillRect(x, y, w, h);
    });
  requestAnimationFrame(draw);
})();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.