What you'll build
Use requestPointerLock() to capture the cursor for FPS-style aiming. Standard in browser game engines.
Steps
- Call canvas.requestPointerLock() inside a click handler
- Listen to mousemove for movementX / movementY
- Update a yaw/pitch state, never an absolute (x, y)
- Call exitPointerLock() on Escape
Why this works
Custom cursors are the smallest, most-viewed piece of UI on a desktop. The technique above applies the smallest possible change that achieves the goal — no extra dependencies, no custom event loop, no accessibility regression. If you change exactly what these steps describe and nothing else, the cursor swap is invisible to assistive tech, undisturbed by browser or OS updates, and free of the memory leaks common to JS-driven cursor implementations.
Common mistakes
- Skipping the hotspot. The hotspot is the pixel inside your cursor image that actually counts as the click point. Defaulting to
(0, 0)is wrong for almost every cursor type. Tune it, then test on a 1-pixel target. - Skipping the fallback. Custom cursor URLs without a fallback keyword are silently ignored by some browsers. Always end the rule with
pointer,auto,crosshair, or whatever system cursor matches the role. - Antialiasing the pixel grid. If you ship a 16x16 PNG and the browser scales it, you lose the pixel art. Ship a 32x32 (2x) variant, or use SVG with
shape-rendering="crispEdges". - Animating idle cursors. Even a great animation makes the cursor feel restless. Reserve animation for busy, working, and progress states.
Testing checklist
- Hover over a white background (Stack Overflow), a black background (a code editor), and a chaotic background (a YouTube grid). The cursor must be visible on all three.
- Verify the hotspot lands where the cursor "feels like" it points by hovering over a tiny target.
- If the cursor is animated, watch it for 30 seconds without context. If it gets distracting, slow it down or remove animation.
- Test in at least two browsers and on a HiDPI display. Cursor handling differs more across browsers than most CSS properties.
Related CSS cursor reference
If you are still firming up your CSS cursor instincts, the CSS cursor reference covers every keyword and value, including cursor: url(), fallback chains, and hotspot tuning.
Pick a pack to try this with
If you want a cursor pack to use as the source for this tutorial, browse the collections page or jump straight to a free CC0 set on the community assets page. Both pages link to packs you can download right now.