Devlog #13 — First Accessibility Audit: What We Found and Fixed

WebGL simulations and accessibility don't traditionally mix well. Canvas elements are opaque to screen readers. Controllers are unreachable by keyboard. Contrast ratios fail at WCAG AA. We finally ran a proper audit — here's the damage report and what we did about it.

Why We Did This Now

Several teachers wrote to us asking whether any of the simulations could be used in classes with visually impaired students. The honest answer was: we didn't know. That was embarrassing enough to motivate an audit. We used Lighthouse, axe DevTools, and manual keyboard-only walkthroughs across every page on the site.

Summary Scores (Before)

67
Lighthouse A11y (avg)
18
axe violations found
4
Critical (WCAG A)
94
Lighthouse A11y (after)

What We Found

🔴Critical
Keyboard trap in simulation canvases
Pressing Tab inside a simulation would tab through Three.js internal DOM elements, never escaping to the page controls. Users relying on keyboard navigation were stuck.
Fix: added tabindex="0" to the canvas wrapper plus an Escape key listener that blurs the canvas and returns focus to the page.
🔴Critical
Missing <label> on all control inputs
Every simulation control panel had <input type="range"> sliders with no associated label — only visual text nearby. Screen readers announced "slider" with no context.
Fix: wrapped each slider in a <label>, or added aria-label and aria-describedby attributes where the label needed to be separate.
🔴Critical
Colour contrast failures (muted text)
Our --color-muted-2 token (#7a88a0) on the dark background (#060d1a) produced a contrast ratio of 3.4:1 — below the WCAG AA requirement of 4.5:1 for normal text.
Fix: lightened --color-muted-2 to #8fa5c0 (4.6:1). Also updated --color-muted similarly.
🔴Critical
No skip-to-content link
Every page forced keyboard users to tab through the entire navigation bar before reaching simulation controls. On pages with 12+ nav items this was unusable.
Fix: added a visually hidden "Skip to main content" link as the first focusable element on every page, visible on focus.
🟡Warning
Canvas has no text description
WebGL canvases are completely invisible to screen readers. A user with VoiceOver would arrive at a simulation and hear nothing useful.
Fix: added role="img" and aria-label to each canvas with a meaningful description of what the simulation shows, plus a link to a text-based explanation.
🟡Warning
Focus indicator overridden by CSS reset
Our global outline: none reset removed the browser focus ring on all interactive elements. Keyboard users had no visual indication of where focus was.
Fix: removed the global reset, replaced with :focus-visible rule using a 2px amber offset outline on all interactive elements.

The Hardest Problem: Moving Simulations

The hardest accessibility challenge with WebGL simulations is not technical — it's conceptual. A screen reader user can't meaningfully experience a flocking simulation or a pendulum. The canvas is a bitmap; there's nothing to narrate.

Our approach was to separate the interactivity from the experience:

This doesn't make our simulations fully accessible to users with visual impairments — that would require a completely different interaction model. But it makes the surrounding context and controls fully accessible, which is a meaningful improvement.

What's Still Outstanding