Smooth out streaming text

Buffer uneven chunks and reveal them at a steady rate.

Sam Selikoff@samselikoff
Hacking on a useBufferedText hook and <AnimatedScroller> component for streaming text. Goal is to have smoother output regardless of the rate + chunk size of the stream.
Oct 14, 2024View on X ↗

1 · Same stream, two displays

Both panes get identical chunks. Raise jitter and replay.

Append each chunkBad
Biggest jump0 charsLongest pause0ms
Buffer, steady revealGood
Biggest jump0 charsLongest pause0msBuffer0
const queue = [];            // chunks land here as they arrive
let shown = 0;

onChunk((text) => queue.push(...text));

function frame() {
  // reveal faster when the buffer is large, so it never lags far behind
  const n = Math.max(1, Math.round(queue.length / 18));
  shown += queue.splice(0, n).join("");
  render(shown);
  if (queue.length || streaming) requestAnimationFrame(frame);
}

Why it matters, pros and cons

Why it matters

Network chunks are uneven, so raw output stalls and then lurches. A buffer decouples arrival from display.

Pros

  • Text flows at the same pace whatever the chunk size or network jitter.
  • Adaptive speed catches up after a burst instead of drifting behind.
  • Works with any stream: the buffer sits between the transport and render.

Cons

  • The reader sees text slightly after it arrives, a few hundred ms at most.
  • Needs a per-frame loop and care to stop it once the buffer drains.
  • Reduced motion users should get whole chunks, not a character race.