Accessibility · Assistive Technology

The Screen Reader Announcement Pipeline

How a screen reader turns an accessibility-tree event into spoken output, from event dispatch through speech synthesis, and where latency and interruption come from.

A screen reader is not a text-to-speech engine, and it is not a browser. It is a client of the accessibility API exposed by the operating system, and its job is narrower than either of those components: it observes a tree of UI objects, filters the stream of events those objects emit, and turns the events worth hearing into speech. Every announcement a user hears is the tail end of a pipeline whose earlier stages are invisible. Understanding that pipeline explains most of the behaviour that frustrates both users and developers, from double announcements to speech that lags half a second behind an action.

The accessibility tree as the source of truth

The screen reader never reads pixels. It reads a data structure called the accessibility tree, which the application or the browser builds from its own UI model. Each node in that tree describes one interactive or informative element: a button, a heading, a text run, a checkbox. A node carries a role, a name, a value, a state, and a set of relationships to other nodes. When a sighted user moves the mouse, the pointer tells the operating system which pixel it is over; when a screen reader user moves focus, the operating system tells the screen reader which tree node is now focused.

The tree is delivered over an inter-process boundary. On Windows the dominant APIs are Microsoft Active Accessibility (MSAA), built around the IAccessible interface, and its successor UI Automation (UIA), which models every element as an IUIAutomationElement with typed property and control-pattern interfaces. On macOS the equivalent is the NSAccessibility protocol family; on Linux and elsewhere the AT-SPI bus carries the same information over D-Bus. Each of these is an event source: when an element gains focus, changes value, or appears, the accessibility API emits an event that the assistive technology has registered to receive.

The reason the tree matters so much is that everything downstream is only as good as it. If a button is exposed as a generic group with no name, the screen reader has nothing specific to say. The pipeline described here assumes a well-formed tree; the failure modes section returns to what happens when it is not.

From event to announcement

The pipeline has four stages, and each one can add latency or drop information.

The first stage is event reception and filtering. An application can emit hundreds of events per second during a single interaction: focus changes, property changes, children being added and removed, text edits. The screen reader subscribes to only a subset, and within that subset it discards events it considers redundant. Two events with the same type and target within a short window are often coalesced, because announcing “value changed” twice in 40 milliseconds is noise. This filtering is why a screen reader can appear to miss a rapid sequence of updates: it deliberately collapsed them into one.

The second stage is resolution. An event carries only a pointer to a node, not the text to speak. The screen reader must walk from that node to assemble the announcement, and this is where the accessible name is computed. For a button, the name is its label or text; for a heading, the text of its subtree; for an input, a name plus a value plus a description. The name computation is itself a nontrivial recursive procedure governed by a specification, and the screen reader performs its own version of it against the platform tree. The cost of resolution is proportional to the depth of the subtree it must visit.

The third stage is language and context processing. The screen reader has a voice and a set of pronunciation rules configured by the user, and it must map the resolved text onto those. Numbers get expanded from digits to words or left as digits depending on the user’s verbosity setting. Abbreviations and symbols are expanded. This stage also decides prosody: where to pause between clauses, whether to rise at a question, and which rate to use. The screen reader emits a markup document, typically in SSML (Speech Synthesis Markup Language), that carries this information to the final stage.

The fourth stage is the synthesis and audio output itself. The SSML goes to a speech engine, which produces audio samples and hands them to the audio device. The screen reader can interrupt this stage at any point: if a new event arrives while the previous announcement is still playing, the screen reader cancels the current utterance and starts the new one. Interruption is the single most important behaviour in the whole pipeline, because it is what keeps speech in step with a user who is tabbing quickly through a form.

Why latency accumulates

Each stage contributes delay, and the delays add because the stages run in sequence for each utterance. Event delivery over the accessibility bus is on the order of a few milliseconds but is not zero. Resolution is the most variable cost: a name that requires walking a deep subtree, or one that triggers a live-region query, can take tens of milliseconds. Synthesis latency is the dominant term for a cold engine; the time from SSML to first audio sample can be in the tens to low hundreds of milliseconds depending on the engine and the platform, and it is shorter when the engine has already synthesized a prefix.

The interaction between interruption and latency explains a familiar symptom. When a user tabs quickly, each new focus event cancels the in-flight utterance. But the cancellation is not instantaneous: the audio already buffered to the device keeps playing for a short time, and the synthesis of the new utterance takes its own startup time. The result is that fast tabbing produces a staccato series of partial words rather than clean full announcements. Screen reader implementers reduce this by pre-synthesizing common phrases and by flushing the audio buffer aggressively, but the buffering cannot be removed entirely because the audio device needs samples in advance to avoid underruns.

A related cost is the round trip for the name itself. In some platforms the accessibility API permits the server to compute the name lazily, in the client process, only when requested. That means the screen reader’s resolution stage can trigger work inside the application’s own process, and if that application is blocked or slow, the announcement stalls waiting for it. This is the architectural reason that accessibility APIs are described as a contract between processes rather than a simple data dump: the data can be computed on either side, and the timing depends on which side does the work.

Failure modes

The most common failure is an announcement that is correct but useless because the tree was wrong. A button rendered as a <div> with a click handler exposes no role and no name, so the screen reader announces “clickable” or the raw text with no verb. The pipeline works perfectly; the input was defective. No amount of speech-engine tuning fixes a missing name.

The second failure is the double announcement. It arises when an application both updates a live region and moves focus, or when an element emits both a property-changed event and a children-changed event for what is semantically a single update. The filter stage cannot always tell that two events mean the same thing, so the user hears the announcement twice. This is why the discipline of choosing one announcement mechanism for one state change matters more than any pipeline tuning.

The third failure is the missed announcement under load. When events arrive faster than the reader can synthesize and speak them, the queue grows, and the reader must drop the oldest or the least important entries. An important announcement can be dropped if it arrived during a burst of unimportant ones. Live-region priority attributes exist precisely so that the filter stage can prefer some events over others under exactly this load.

The final failure is drift between speech and reality. Because resolution and synthesis take time, a user can act on stale speech: the reader announces “selected” just as the user’s next keystroke changes the selection again. This is inherent to any asynchronous pipeline and is mitigated, not eliminated, by interruption and by the ordering guarantees the accessibility API provides.

Where the other pieces fit

The accessible name computation at the resolution stage is a subject large enough to deserve its own treatment, as is the way a reader maintains a virtual buffer for document navigation rather than relying on focus alone. The announcement pipeline is the spine that connects those pieces: it defines the contract every other mechanism ultimately speaks through.

References

  1. Web Content Accessibility Guidelines (WCAG) 2.2 — W3C
  2. MSAA and UIA: the Windows accessibility APIs — Microsoft
  3. SSML 1.1 Specification — W3C