canvas + screen readers

War, Announced

A game of War drawn entirely on a canvas, which a screen reader cannot see at all. So every flip is announced out loud through a hidden status zone, the same technique Classic Scoundrel uses. Cover the canvas and play it by ear.

The game

Fifty two cards, split evenly. Flip the top cards, higher rank takes both, ties mean war. Everything that happens is written to a hidden live region, and mirrored into the transcript so sighted visitors can watch an invisible feature work.

Your browser needs canvas support to draw the game. The announcements still work: use the Flip button and read the transcript.

Round 0 路 You 26 cards 路 Computer 26 cards

Keyboard: press F to flip. Space and Enter also work when a button is focused.

What the screen reader hears

A mirror for sighted visitors only. This list is deliberately not a live region, so screen reader users hear each announcement once, from the hidden status zone, not twice.

  1. Nothing yet. Flip a card.

Why this page exists

The problem

A canvas is a rectangle of pixels. Games love it because anything can be drawn there. Screen readers cannot use it, because assistive technology reads the document tree, and a canvas has almost no tree to read. Every card in the game above, every score, every win, is just paint. A blind player can find the buttons on this page and press them, and still learn nothing about what happened, because the results are only ever drawn.

The technique

The fix is one hidden element and one habit. The element is a status zone: a visually hidden region marked role="status" with aria-live="polite", so a screen reader speaks whatever is written into it without interrupting. The habit is to write a short sentence into it for every event that is worth drawing, in the game's own words, with the score attached:

<div id="sr-announcer" class="sr-only" role="status"
     aria-live="polite" aria-atomic="true"></div>
function announce(message) {
  const el = document.getElementById('sr-announcer');
  el.textContent = '';
  setTimeout(() => { el.textContent = message; }, 40);
}

The repeat gotcha

Live regions only speak when their text changes. Flip the same winning card twice in a row and the second announcement is identical to the first, so the screen reader stays silent and the player misses a whole round. That is why announce() clears the region first and writes on a short delay. The clear and rewrite is what makes a repeated event get spoken again. It is a small detail, and it is the difference between a player following the game and a player guessing.

Focus tells you where you are. This tells you what happened.

Keyboard support and focus management answer the question "how do I act, and where am I?" They say nothing about outcomes. Announcements close that loop: act by keyboard, hear the result. Neither half is optional. Together they make a canvas game a game again for someone who cannot see it.

Exactly what Classic Scoundrel does

This is not a simplified cousin of the real thing. Classic Scoundrel, a full roguelike card game drawn on canvas, uses the same hidden status zone and the same clear-then-write announce call for every card play, potion, room change, and game over. War is just the smallest game that proves the point. If an event is worth drawing, it is worth saying. On canvas, the developer is the accessibility tree. There is nobody else.

Play Classic Scoundrel, the game this technique comes from.