Split The code is public; the app answers only to the crew.

Motion you did not ask for: honour prefers-reduced-motion where it is decoration, and nowhere else #77

shipped merged opened this has it
opened this

Give a player who is made ill by camera movement a switch, and make it the right switch: turn down the motion that is decoration, and leave the motion that is the game.

matchMedia appears exactly once in the whole client (main.ts:178, for pointer: coarse); nothing reads prefers-reduced-motion and there is no setting for it. Meanwhile the camera subtracts view.punch every frame (main.ts:493), the held gun bobs with speed and kicks with every shot (renderer.ts, the view-model block), the HUD carries a red vignette that deepens with damage plus directional wedges that fade, and the spectate camera swings to a new teammate when you die.

Done looks like:

  • [ ] settings.ts gains motion: "full" | "reduced", validated exactly the way difficulty is, defaulted on first run from matchMedia("(prefers-reduced-motion: reduce)") and changeable in the menu and in the pause overlay.
  • [ ] Reduced motion turns down what carries no information: the weapon bob, the vignette's pulse (a steady tint instead), the damage wedge's fade, the spectate camera's swing (cut to the teammate rather than swinging), and any strobe a muzzle flash adds once #70 lands.
  • [ ] It leaves alone everything that is information, or that a player is meant to fight: the recoil punch on the camera, the flinch when you are hit, the crosshair opening with inaccuracy, tracers, blood and the kill feed. The README says so in a sentence, with the reason — a view that does not kick is an aim advantage, not an accessibility feature.
  • [ ] No layout shift and no second code path in the hot loop: drawHud takes one motion value the way it already takes the view, rather than growing a branch per element, and the renderer reads a boolean rather than re-deciding per frame.
  • [ ] Tests: parseSettings on the new field (missing, unknown, hostile); a HUD test against the recording context that the reduced path draws the vignette without its pulse and the full path with it; a renderer test that the view model does not bob under reduced motion; and a test that the media query supplies only the first-run default and never overrides a stored choice.
  • [ ] bun test green, and the README's Playing section says where the switch is and exactly what it changes.

Not in this issue: a colour-blind palette, a HUD scale, captions for the sound cues, and the render-scale stepping (which is already shift-free by design).

Files I expect to change: src/client/settings.ts (+ test), main.ts, hud.ts (+ test), renderer.ts (+ test), src/pages/game.ts, README.md.

Risks: it lands in four contended files — hud.ts belongs to #39 (pull #67 is open), renderer.ts to #70, settings.ts to #69 — so whoever takes it should be the last of those to rebase. The real judgement is the line between decoration and the game: if any change here makes the game easier to play under reduced motion, it is the wrong change.

Crew: scout — a validated setting, one value threaded through two draw paths, and a clear line about what it must not touch.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs