Get AI summaries of any video or article — Sign up free
Claude Code has a big problem thumbnail

Claude Code has a big problem

Theo - t3․gg·
5 min read

Based on Theo - t3․gg's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Claude Code targets ~60 fps, leaving only about 5 ms for converting the computed UI into ANSI output after scene-graph work.

Briefing

Claude Code’s flicker and sluggishness trace back to a tight rendering budget and a terminal environment that punishes “React-style” updates—especially when the app must redraw or re-layout large portions of the UI under real-world conditions like resizing, long scrollback, and frequent garbage collection.

A key detail from the internal engineering discussion is the frame budget: Claude Code targets roughly a 16-millisecond cadence, leaving only about 5 milliseconds for the terminal-specific work that turns the computed UI into ANSI sequences. That narrow window matters because terminal rendering isn’t just “slower DOM.” Terminals are effectively byte- and escape-sequence limited: updating earlier content requires sending additional control codes to reposition the cursor and overwrite prior lines, which can balloon the amount of data sent and the amount of state the renderer must track. When the renderer misses its frame budget, flicker and jitter show up.

The performance debate also hinges on how React’s virtual DOM model behaves in an append-only output system. React’s strength comes from diffing and committing changes when it can update the “real world” representation efficiently. In a terminal, the “real world” is built by writing text and escape sequences; to change something, the system often has to locate the right region in scrollback and overwrite it. That means React-driven re-renders can translate into large bursts of terminal output—even when only a small part of the UI changed—because the terminal must be coerced into the right state via escape commands.

Resizing exposes another mismatch. Browsers provide explicit resize events and well-defined layout feedback loops. Terminals don’t offer the same standardized signals, so Claude Code has to infer dimensions and patch behavior around terminal quirks. The discussion points to intercepting SIGWINCH, disabling scrolling while measuring the pseudo-terminal size, recalculating layout, then patching differences and restoring scroll behavior. This is the kind of work that makes the experience feel “browser-like,” but it also increases complexity and the chance that rendering falls behind.

Garbage collection becomes a practical failure mode. In some terminal/OS combinations, the rendering pipeline triggered GC too often—an issue that only appears after shipping to diverse environments. When GC pauses land inside the frame window, the renderer misses deadlines, producing flicker.

The transcript also contrasts Claude Code with other terminal UI approaches. Libraries like ncurses and modern Rust TUIs (e.g., ratatouille, used by OpenAI’s Codex alternative) can rely on a standard scroll buffer and different rendering strategies. Some apps use “alt mode” to avoid polluting the terminal’s normal scrollback, which changes selection, copy/paste, and scroll behavior—often requiring custom selection logic and clipboard handling.

Overall, the argument isn’t that React is inherently wrong for terminals, but that the terminal’s constraints make React’s diff-and-rasterize pipeline expensive in ways that are easy to underestimate. The engineering goal, as framed here, is less about raw milliseconds and more about maintainability and safe iteration—keeping changes from breaking other parts of a large codebase while still meeting a 60 fps target. The result: Claude Code can be usable day-to-day, but its worst-case rendering paths are where the system’s architectural tradeoffs show up as lag, flicker, and jitter.

Cornell Notes

Claude Code’s rendering problems come from a mismatch between React-style UI diffing and the realities of terminal output. With a 16-millisecond frame budget, only about 5 milliseconds remain for converting the computed UI into ANSI sequences, so any extra work—like large re-renders, escape-sequence-heavy updates, or garbage collection pauses—can cause flicker. Terminals also lack browser-like resize events and standardized layout signals, forcing Claude Code to intercept SIGWINCH, measure pseudo-terminal dimensions, and patch scroll behavior. The transcript argues that these constraints make “small UI changes” potentially translate into large bursts of terminal bytes, especially when the app must overwrite earlier content in scrollback. The engineering tradeoff is maintainability and safe iteration using React, even if some terminal rendering paths are slower than ideal.

Why does a 16-millisecond frame budget matter for Claude Code’s flicker?

Claude Code targets about 60 fps, which implies a ~16 ms cadence per frame. The internal framing cited leaves only ~5 ms for the terminal-specific step: generating and writing the ANSI sequences after building and diffing a scene graph. If the pipeline (including layout, diffing, and output generation) exceeds that window, the terminal update misses the deadline, and the user perceives flicker or jitter.

How does React’s virtual DOM model become costly in a terminal compared with a browser?

React’s virtual DOM is efficient when it can diff and commit changes to a real representation that’s designed for frequent updates. In terminals, updates are produced by writing text plus escape sequences. Changing earlier content often requires cursor movement and overwriting via control codes, which increases the number of bytes sent and the complexity of tracking terminal state. If React re-renders more than strictly necessary, the terminal can end up receiving large output bursts even for small UI changes.

What makes terminal resizing particularly hard for terminal UIs?

Browsers provide standardized resize signals and layout feedback loops. Terminals don’t offer the same windowing model; instead, apps must infer dimensions from the pseudo-terminal and handle scrollback behavior. The transcript describes intercepting SIGWINCH, temporarily disabling scrolling to measure the terminal size, recalculating layout, patching differences, and then re-enabling scrollback—work that can be expensive and timing-sensitive.

Why does garbage collection show up as a rendering bug in some setups?

The transcript notes that some environments triggered garbage collection too often inside the rendering pipeline. GC pauses can interrupt the frame loop; when a GC pause lands within the frame budget, the renderer misses its timing target and the terminal output becomes visibly unstable (flicker/jitter). This kind of issue can be hard to catch until real users run on diverse terminal/OS combinations.

What’s the practical difference between using the standard scroll buffer and terminal alt mode?

Standard buffer mode appends output to the terminal’s scrollback, so selection and scrolling behave like typical terminal history. Alt mode avoids polluting the standard scrollback, which can make the UI feel more controlled, but it changes user interactions: selection may not work automatically, and the app may need custom selection and clipboard logic. The transcript also notes that some apps clear or override history by sending additional terminal commands, which is non-trivial.

How does the transcript connect maintainability to the choice of React for Claude Code?

React’s component model and top-down update semantics can reduce certain classes of bugs and make it easier for many engineers to contribute without breaking unrelated parts of the UI. The transcript frames the team’s optimization as balancing user experience targets (like 60 fps) with safe iteration speed—accepting that some terminal rendering paths may be slower because the architecture supports large-scale development and fewer cross-component regressions.

Review Questions

  1. What specific steps in a terminal rendering pipeline can consume the limited ~5 ms available after the React/scene-graph stage?
  2. Why might a terminal UI that relies on escape-sequence overwrites send far more data than the number of visible characters suggests?
  3. How do SIGWINCH handling and scrollback behavior interact during terminal resizing, and why can that lead to missed frames?

Key Points

  1. 1

    Claude Code targets ~60 fps, leaving only about 5 ms for converting the computed UI into ANSI output after scene-graph work.

  2. 2

    Terminal rendering is constrained by bytes and escape-sequence operations, not just by the number of visible characters.

  3. 3

    React-style re-rendering can translate into large bursts of terminal output because terminals require cursor positioning and overwriting to update earlier content.

  4. 4

    Resizing is harder in terminals than in browsers because apps must infer pseudo-terminal dimensions and patch scroll behavior without standardized resize events.

  5. 5

    Garbage collection pauses can directly cause missed frame deadlines, producing flicker in some terminal/OS combinations.

  6. 6

    Using standard scrollback versus alt mode changes user interaction behavior (selection, scrolling, clipboard) and often forces custom logic.

  7. 7

    The engineering tradeoff prioritizes safe, fast iteration and maintainability while still aiming to meet a 60 fps experience target.

Highlights

A 16 ms frame budget leaves only ~5 ms for terminal output generation; missing that window shows up as flicker.
Terminals are effectively byte/escape-sequence limited, so “small UI changes” can still produce large output when overwriting prior content.
SIGWINCH-based resizing handling and scrollback patching are central to making Claude Code behave responsively in terminals.
Garbage collection triggered too often in some environments, turning performance issues into visible rendering instability.
The maintainability argument: React’s component model helps teams move quickly without breaking unrelated UI parts, even if terminal rendering is not its natural fit.

Topics

  • Claude Code Rendering
  • ANSI Frame Budget
  • React Virtual DOM
  • Terminal Scrollback
  • SIGWINCH Resizing

Mentioned