Did you know that there are two completely separate systems that determine when live regions are spoken? They are:
- Screen reader speech queue (assistive technology layer)
- System-driven scheduling (browser / event loop level)
Assertive announcements 1 are controlled almost entirely by the screen reader.
Polite announcements 2 depend on both systems, which is why their timing can vary so much in practice.
Let’s look at these two systems in detail.
1. Screen reader speech queue (assistive technology layer)
Screen readers manage their spoken output using an internal speech queue.
An item spoken by the screen reader is called an utterance. This is a single self-contained chunk of speech.
The queue contains:
- Utterances from user navigation (“Heading level 2 ...”)
- Speech produced by reading text (“The quick brown fox ...”)
- Dynamic announcements (ARIA live region updates)
- Semantic hints (“button”, “selected”, “expanded”)
This queue is generally processed FIFO (first in, first out) unless specific rules override it.
Assertive announcements:
- Immediately flush active and pending utterances.
- Interrupt whatever is being spoken.
- Insert the assertive utterance at the front.
- Speak it next.
Assertive always wins because “interrupt behaviour” is part of the queue layer.
Polite announcements:
- Are added to the end of the speech queue.
- Never interrupt the user.
- Only play when the queue becomes empty.
- Wait behind navigation, reading, search/cursor operations, etc.
This means user activity always delays polite announcements. But screen readers do not wait for sentences or paragraphs. They only wait until their queue empties.
This explains the first half of why polite announcements vary.
2. System-Driven Scheduling (Accessibility Subsystem Layer)
Before a screen reader can speak anything, the browser must:
- Detect the DOM mutation 3.
- Update the accessibility tree.
- Decide when to fire an AX event 4.
- Deliver that event to the OS accessibility API.
- Which then delivers it to the screen reader.
This pipeline runs on the browser's accessibility subsystem, a system that operates independently of the JavaScript engine, often on its own thread or process. It is not a stage inside the JavaScript event loop, and the event loop does not schedule or gate AXEvents.
What the event loop does control is when your JavaScript runs and when it yields control back to the browser:
Rendering (layout, paint, compositing) and AX tree updates are separate subsystems again. They are not part of event loop management, but their timing is indirectly affected by it: a DOM mutation cannot be detected by the accessibility subsystem until the JavaScript that caused it has actually run, and the event loop determines when that happens. Once JavaScript yields, the accessibility subsystem is free to detect the mutation and act on its own schedule, independently of any further event loop activity.
This is the other half of live region timing: not the event loop itself, but the independent, less-predictable systems that pick up where the event loop's involvement ends.
Let’s explore how assertive and polite differ at the scheduling layer.
Assertive Timing
Assertive live region AX events tend to be detected and fired soon after the triggering script yields:
- JavaScript-driven DOM mutation 7.
- Call stack becomes empty, microtasks drain 8.
- JavaScript yields control back to the browser.
- The accessibility subsystem detects the mutation and, in most implementations, does not wait for a render tick before generating the AX event.
- ASSERTIVE AX EVENT TYPICALLY FIRES AROUND HERE ←
- Rendering may happen before, during, or after this, independently.
- Task queue work happens later.
Why assertive tends to be fast:
- It generally does not wait on a render tick the way polite updates do.
- It is not typically postponed by animations, layouts, or timers, though browsers may still batch or coalesce AX events internally.
The main thing that can delay the JavaScript side of this is microtask starvation, where microtasks keep queuing more microtasks, so the browser never yields and the mutation is never handed off for the accessibility subsystem to detect. Beyond that, actual AX event timing is implementation-dependent and not something the event loop guarantees.
This explains why assertive is comparatively fast and consistent in practice, though not because the event loop is driving it, only because it has fewer independent systems to wait on downstream.
Polite Timing
Polite live region AX events are intentionally delayed, typically until the browser reaches a stable render state:
- JavaScript-driven DOM mutation.
- Call stack becomes empty, microtasks drain, JavaScript yields.
- The accessibility subsystem waits for a stable render state before generating the AX event.
- POLITE AX EVENT FIRES ←
- Task queue work happens later.
Polite must wait for a future render tick, and a "stable render state" can be delayed by:
- active layouts
- reflows
- animations
- transitions
- scrolling
- microtask storms
- React/Vue batching
- timers
- heavy CPU load
- compositor operations
- pending task queue events
These delays affect the scheduling of the announcement inside the accessibility subsystem, before the screen reader ever receives it. The event loop's only role here is upstream: making sure the DOM mutation itself has actually happened before any of this can begin.
Putting Both Systems Together
This unified pipeline shows exactly where delays occur:
DOM Mutation
↓
Browser Scheduling Layer
(when to fire AX events)
↓
AX Event Fired
↓
Screen Reader Speech Queue
(what gets spoken & in what order)
↓
TTS Utterance Output
Assertive announcements:
- Fast scheduling (after microtasks).
- Immediate queue flush.
- Spoken right away.
Polite announcements:
- Delayed scheduling (after future render).
- Added to end of queue.
- Spoken only after all user-driven utterances finish.
This is why polite announcements vary so much more than assertive ones.
Caveats and nuances
- AX event timing may vary: Browsers may batch, coalesce or optimise accessibility events internally, so timing may not always follow a simple assertive/polite pattern.
- Screen reader behaviour differs: Each assistive technology has its own rules for interrupting, queuing and speaking updates, and these behaviours can vary across versions and operating systems.
- Rendering stability is implementation-dependent: What counts as a “stable render state” differs between browser engines, especially during animations, transitions, layout changes or heavy CPU activity.
- Heavy JavaScript can delay announcements: Long tasks, microtask storms or blocked main-thread execution can postpone both assertive and polite updates.
- Results may vary: Timing differs across Blink, WebKit and Gecko due to differences in layout, rendering and accessibility pipelines.
- Always test across AT and browser combinations: Real-world timing depends on the interaction between JavaScript, browser scheduling, OS accessibility APIs and the screen reader’s own queue.
Related articles
What is an AXEvent?
Detailed explanation of AXevents.
What really happens when a user clicks an accordion button?
A more detailed explanation of an event.
What are DOM mutations?
DOM mutations are any changes to the accessibility tree.
End-to-end browser and accessibility event architecture
A full explanation of how browsers and assistive technologies communicate.
Footnotes
-
[1]
Assertive announcements: Announcements made using
aria-live="assertive". [Back] -
[2]
Polite announcements: Announcements made using
aria-live="polite". [Back] - [3] DOM mutation: Any change to the DOM — whether triggered by JavaScript, user interaction, or browser behaviour — such as adding, removing, or updating elements or attributes. [Back]
- [4] AX event: An accessibility event fired by the browser when something meaningful changes in the accessibility tree, such as focus, name, value, or state changes. [Back]
- [5] The call stack: A temporary structure the JavaScript engine builds while running code, showing which functions are currently being executed. It grows as functions call other functions and shrinks as they return. [Back]
- [6] Microtask queue: A queue of small, high-priority callbacks (e.g. Promise handlers, MutationObserver notifications) that must run immediately after the current JavaScript task finishes and before rendering or the next task begins. [Back]
- [7] JavaScript-driven DOM mutation: A DOM change caused directly by JavaScript code, such as creating elements, updating attributes, or altering text content. [Back]
- [8] Microtasks drain: The point in the event loop when all queued microtasks must be executed to completion before the browser is allowed to render or pick up the next task. [Back]