# UniversalMediaController

[Return to UniversalMediaController Home](index.html)

A universal, screen-reader-first controller for accessible audio and video across existing web media players.

## Purpose

Universal Media Controller provides one consistent, keyboard-first and screen-reader-first control interface for supported web media players. It does not attempt to repair an entire website or replace the underlying media service. It communicates with supported players through defined adapters and presents users with predictable controls, focus behavior, names, states, and announcements.

## Defining requirements

- Every controller instance operates exactly one media item.
- Every controller is programmatically associated with the media it operates.
- Every media item has a meaningful accessible title.
- The controller attempts to determine the media title from reliable page or provider information when the site owner has not supplied one directly.
- The first focusable control includes the media title in its accessible name so users navigating by form elements or buttons receive the media identity immediately.
- The media title is not repeated in every later control name.
- Unidentified media is treated as an implementation problem and is reported to the site owner.

## Current status

**Working HTML5 proof of concept, refined from real JAWS testing.** Version 1 begins with native HTML5 audio and video. Recent work delivers:

- A reusable core controller (`Packages/Core/universal-media-controller.js`), independent of any specific media platform, driven by a centralized state machine (`Packages/Core/controller-state.js`) instead of scattered per-control logic.
- A capability model: the controller asks the connected adapter what it supports (`supportsAudioDescription()`, `supportsCaptions()`, `supportsTranscript()`, `supportsPlaybackSpeed()`, `supportsSeeking()`, `supportsVolume()`, `supportsMute()`, `supportsRestart()`, `supportsBookmarks()`, `supportsChapters()`) and builds/gates controls accordingly, rather than branching on adapter type.
- Accessibility-first control order: Audio Description, Captions, and Transcript appear before any playback control. The first focusable control is Audio Description and carries the media title in its accessible name; later controls stay concise.
- Unsupported controls (Audio Description and Transcript in this milestone's HTML5 adapter, or Captions when the current media has no track) stay in the page and in tab order as ordinary, fully enabled buttons rather than being removed or modeled as disabled -- activating one announces that the feature is not available instead of doing nothing. This is the result of three rounds of real JAWS/NVDA testing and fixes; see `Docs/Limitations.md`, "Issue 1" and "Milestone 5," for the full history and what is still awaiting re-verification.
- Playback speed is a primary, always-visible control (0.5x-2x in quarter-step increments, plus normal), never hidden behind a settings menu.
- All Version 1 controls: audio description, captions, transcript, play/pause, restart, skip backward/forward 10 seconds, playback speed, mute/unmute, volume, and current time/duration.
- A single controlled accessible-status mechanism per controller (no competing live regions), with refined announcement wording ("Playback started", "Playback paused", "Playback restarted", "Playback complete", "Captions enabled"/"disabled", "Audio Description enabled"/"disabled", "Playback speed 1.5 times").
- Working examples under `Examples/HTML5/`: a video example, an audio example, a low-confidence-title fallback demonstration, and a multiple-controllers-on-one-page demonstration -- simplified to behave like a media experience rather than a technical article, with implementation details moved into the documentation.
- The architecture is ready to accept a first external-player adapter without further core changes: `UniversalMediaController` accepts an explicit `adapter` instance via its constructor options, at which point it no longer requires its media item to be an HTMLMediaElement. See `Docs/Adapter Contract.md`, "External player readiness".

**Experimental: a first external-player adapter now exists.** `Packages/Core/adapters/vimeo-media-adapter.js` implements the full adapter contract against Vimeo's official Player SDK, with no Vimeo-specific logic added to the core controller. This proves the architecture against a real third-party player, but **has not been tested against a live Vimeo embed or with real assistive technology** in this development environment (no network access to vimeo.com here). It is verified with an automated mock-based test suite (`Tests/Vimeo/run-mock-tests.js`, 14 passing assertions) against a mock Player object built from Vimeo's documented SDK, not against the live service. See `Docs/Limitations.md`, "Vimeo Adapter Milestone 1," for the complete, honest account of what is and is not verified, and `Examples/Vimeo/vimeo-example.html` for the demonstration (marked Experimental on the landing page and in the roadmap).

External players beyond Vimeo (YouTube, Brightcove, Panopto, Wistia, etc.), the React/Vue/Web Component packages, and several other items remain unimplemented by design -- see `Docs/Limitations.md` for the complete list of what is postponed versus what was discovered as a gap during implementation.

See `Docs/Getting Started.md` for how to run the examples and integrate the controller into a page, or open `index.html` for the project's landing page.

## Testing the HTML5 examples

The example pages are ordinary web pages -- no build step is required to run them. They do need to be served over HTTP rather than opened directly as a `file://` path, because they use native ES modules (which browsers block from `file://` for security reasons) and because `<video>`/`<audio>` seeking requires a server that honors HTTP Range requests.

From the repository root, run:

```
python Tools/serve-examples.py
```

(On macOS or Linux, this may need to be `python3` instead of `python`, depending on how Python was installed.)

Then open `http://localhost:8000/index.html` in Chrome for the project landing page, or go directly to `http://localhost:8000/Examples/HTML5/video-example.html`.

**Why not just `python -m http.server`?** That built-in server does not support HTTP Range requests, and Chrome silently refuses to seek within `<video>`/`<audio>` without them -- even after the file has fully downloaded. Every other control still works, but skip backward, skip forward, and restart would appear to do nothing, with no error message anywhere. `Tools/serve-examples.py` is a small, dependency-free script (only the Python standard library) that adds the missing Range support so seeking works correctly during testing. It requires no installation beyond Python itself.

See `Docs/Manual Screen Reader Test Plan.md` for step-by-step test sequences written for a screen reader user testing with JAWS or NVDA, including what to specifically re-check following the most recent rounds of real JAWS and NVDA testing. `Tests/Diagnostic/focus-order-diagnostic.html` is a minimal, framework-independent page for testing the not-available-button focus-order pattern in isolation -- test this first.

## Testing the Vimeo adapter (experimental)

`Tests/Vimeo/run-mock-tests.js` runs 14 automated assertions against a mock Vimeo Player object (not the live service). It needs Puppeteer installed (`npm install puppeteer` in whatever environment you run it from) and the local server already running:

```
python Tools/serve-examples.py
node Tests/Vimeo/run-mock-tests.js
```

This proves the adapter's own logic is correct against Vimeo's *documented* API shape. It does not prove anything about the live Vimeo service or real assistive technology -- see `Docs/Limitations.md`, "Vimeo Adapter Milestone 1," before treating this adapter as verified.

## Documentation

- `Docs/Vision.md`
- `Docs/Version 1 Requirements.md`
- `Docs/Architecture.md`
- `Docs/Adapter Contract.md`
- `Docs/Keyboard Interaction Model.md`
- `Docs/Screen Reader Behavior.md`
- `Docs/Accessibility Goals.md`
- `Docs/Supported Players.md`
- `Docs/Universal Media Controller Design Philosophy.md`
- `Docs/Getting Started.md`
- `Docs/Limitations.md`
- `Docs/Manual Screen Reader Test Plan.md`
- `Docs/User Experience Principles.md`


## Accessibility-first philosophy
Accessibility resources are presented before playback controls. Playback speed is never hidden behind a settings menu. The first focusable control identifies the associated media.
