Skip to content
artificia11y Amplifying everyone

Dynamic and streaming output

Streaming output is text that appears piece by piece as the model produces it, rather than arriving all at once. For a sighted user it feels fast and alive. For a screen reader user it is the single most common way AI interfaces break, because the page is changing many times a second.

  • Streaming output updates the page many times a second, which is the most common way AI interfaces break for screen reader users.
  • Use one pre-existing, polite live region rather than inserting a new one when the response begins.
  • Announce in meaningful chunks like sentences, or just the start and end, not every token.
  • Leave focus where the user put it while text streams, and offer a way to jump to the response.
  • Always give a clear, keyboard-operable stop control that is reachable while the text is still streaming.

If you put generated text in a live region and let every token update it, a screen reader tries to announce each change. The result is a barrage that no one can follow, which this site calls announcement flooding. The fix is not to remove the live region but to control what it announces and how often.

Build on one pre-existing, polite live region

Section titled “Build on one pre-existing, polite live region”

Streaming announces through the same kind of live region as everything else, so build it the way Live regions and announcements describes. Put one polite region in the DOM ahead of time, prefer a plain element with role="status", and if you use an output add an explicit role="status" to it. What follows is what streaming adds on top of that shared recipe, because the text arrives token by token.

Rather than announcing every token, announce in units a person can take in, such as a complete sentence or paragraph, or wait until the response settles and announce that it is ready. A good default is to show the text streaming visually for sighted users while announcing the response in larger, debounced chunks for screen reader users, so both audiences get a version that suits them.

Consider announcing only the start and the end

Section titled “Consider announcing only the start and the end”

For many cases the calmest experience is to announce that the assistant is generating, stay silent while the text streams, and then announce that the response is ready so the user can read it at their own pace. James Scholes argues that you should design the screen reader experience deliberately rather than delegating it to a role like log, whose live behavior is inconsistent across screen readers. Decide what the user should hear, and build exactly that.

This is the more common case, and it is worth checking which one you actually have. Plenty of chat interfaces animate the reply for sighted users but receive it from the server as one complete piece, with no real token-by-token stream to chunk. If that is your situation, skip the chunking recipe below and use the whole-reply pattern in Conversational interfaces directly. Its live region carries the settled reply once rather than in sentence-sized pieces. Reach for the chunking recipe only when text is genuinely arriving over time and a person would otherwise wait in silence for it to finish.

Do not move focus as the text streams, and do not trap it inside the growing response. Leave focus where the user put it, announce that the response is ready, and offer a way to jump to it for users who want to read from the top. This connects to the focus requirements in WCAG Operable.

A streaming response can be long, so a user has to be able to stop it. Give a clear, keyboard-operable stop control that is reachable while the text is still streaming, as covered in WCAG Operable.

Audience: Engineer

The recipe below is for a response that genuinely arrives token by token. If your model call resolves with the full reply and only the display animates, there is no stream to chunk, so use the simpler whole-reply recipe in Conversational interfaces instead.

The recipe is a single pre-existing polite live region, updated with debounced chunks the size of a sentence or paragraph rather than tokens, with focus left alone and a real stop button wired to cancel the stream. Do not reach for role="log" and assume it works, since its behavior varies across screen readers. Test with a real screen reader, not only an inspector, because flooding only shows up when something is actually speaking.

Show the text streaming for sighted users in one element, and announce settled chunks through a separate output element that carries role="status", which makes its live region announce reliably across screen readers.

<!-- Sighted users watch this grow token by token -->
<div id="response"></div>
<!-- Screen readers hear settled chunks from this pre-existing region -->
<output role="status" id="sr-announce" class="visually-hidden"></output>
<button id="stop" type="button">Stop generating</button>
// Announce whole sentences, not every token
let pending = '';
function onToken(token) {
document.getElementById('response').append(token); // visible stream
pending += token;
const end = pending.lastIndexOf('. ');
if (end !== -1) {
document.getElementById('sr-announce').textContent = pending.slice(0, end + 1);
pending = pending.slice(end + 1);
}
}

Leave focus where the user put it while this runs, and wire the stop button to actually cancel the request.

Audience: Accessibility Specialist

This is a place where automated tools tell you almost nothing, so test with real assistive technology. Listen to a full streamed response with a screen reader and check that it is announced in a way you can follow, that it does not repeat or talk over itself, and that focus does not jump. Try a long response and a stopped one, since both stress the live region differently.

Audience: Self-advocate

Streaming is the feature that most often turns a useful tool into an unusable one with a screen reader. Either nothing is announced and you do not know an answer arrived, or every fragment is announced and it is a wall of noise. What works is being told the assistant is generating, then being told the answer is ready, and being able to read it yourself without your focus being dragged around.

Audience: Designer

Design a clear done state, not only the streaming animation. Sighted users get progress from the motion, but everyone needs an unambiguous signal that the response has finished, expressed as text and not just the absence of a spinner. Pair that with a visible, persistent stop control during generation.