ai newsMi., 21. Okt.
EilmeldungInception says Mercury 3 beats GPT-6 Luna and Claude Haiku 5.5 at up to 8x the speed

Why Claude Code Flickered: DEC Mode 2026, Cell Diffs and a 16 ms Frame Budget

Book: System Design Pocket Guide: Fundamentals Also by me: Thinking in Go (2-book series) — Complete Guide to Go Programming + Hexagonal Architecture in Go My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools Me: xgabriel.com | GitHub GitHub is

dev.toMittwoch 00:01 · 21.10.8 Min. Lesezeit
Bild: dev.to

GitHub issue #769 on the Claude Code repo, "In-progress Call causes Screen Flickering", was opened on April 12, 2025. It is still open. Peter Steinberger called it Claude Code's signature flicker: the Claude Code screen blinking as tool output streamed in.

On December 17, 2025, Anthropic's Thariq Shihipar started a thread with "We've rewritten Claude Code's terminal rendering system to reduce flickering by roughly 85%" ( thread list, same text reposted by Boris Cherny ). The fix was rolled back. On January 21, 2026 he posted that it was rolled out again to everyone, with a description of the pipeline. One number from it is worth remembering: a ~16 ms frame budget, with roughly 5 ms to get from the React scene graph to ANSI written.

That 85% is the team's own figure, and 85% is not 100%. What remains depends on whether your terminal knows when a frame ends.

A terminal is a grid of cells. A program like Claude Code doesn't draw pixels. It writes bytes: printable text, plus escape sequences that move the cursor, erase lines and change colours. The terminal parses that stream into its grid and paints the grid to the screen on its own schedule.

The terminal has no idea where your "frame" ends. If an app moves the cursor up, erases ten lines and writes ten new ones in several writes, the terminal is free to paint after any of them. Catch it after the erase and you see a blank block. Catch it halfway and you see half the old frame under half the new one. At a typical 60 Hz paint rate, that is flicker.

Chris Lloyd, writing about "our pipeline" on Hacker News, said most people picture Claude Code as "just a TUI" when it should be closer to "a small game engine". Each frame, the pipeline builds a scene graph with React, lays it out, rasterizes it to a 2D screen, diffs that against the previous screen, and only then turns the diff into ANSI sequences. Diffing at the cell level means fewer bytes per frame and fewer chances to be caught mid-update, but any frame that still takes more than one write can be painted half-done.

Where the conversation lives matters too. The Claude Code docs say the classic renderer keeps the conversation in your terminal's native scrollback, while the newer fullscreen renderer draws on the alternate screen buffer, like vim or htop, and only renders messages that are visible. The docs say the difference is most noticeable where rendering throughput is the bottleneck, naming the VS Code integrated terminal, tmux and iTerm2. The same page describes fullscreen, still a research preview, as the mode that eliminates flicker. To try it, run /tui fullscreen inside a session, or start with CLAUDE_CODE_NO_FLICKER=1 claude; /tui default switches back.

The fix for "the terminal painted too early" is to tell the terminal when a frame starts and ends. That is DEC private mode 2026, synchronized output, written up by Christian Parpart in the Contour terminal's spec.

It is double buffering, with the app saying when to swap. The spec admits there is no consensus on how long a terminal should wait for the end marker before giving up, and notes that a timeout that is too short is no worse than not syncing at all.

You can ask a terminal whether it knows the mode with DECRQM. In bash:

printf '\e[?2026$p' IFS = read -rs -t 1 -d y reply printf '%q\n' " $reply " Enter fullscreen mode Exit fullscreen mode Look at the digit after the semicolon in the reply. Per the spec, 1 or 2 means the mode is supported (currently set or reset), 0 means not recognized, and 4 means permanently reset, which also means no. No reply within the second counts as unsupported. Claude Code does this probe itself: the docs say it checks for synchronized-output support at startup and uses it when the terminal reports it.

On March 22, 2026, a user filed #37283: Claude Code 2.1.81 in tmux 3.4 split panes, under WSL2 and Windows Terminal, flickered whenever it streamed output, with the cursor jumping to the top of the render area and back on every update. Idle panes stayed still. The reporter traced it to redraws not wrapped in DECSET 2026 and proposed the obvious fix: emit \e[?2026h before the cursor moves and clears, \e[?2026l after.

The catch is that tmux is a terminal too. It parses your app's output into its own grid for each pane, then redraws that grid to your real terminal. So the app's markers have to be understood by tmux, not forwarded. The sync value in tmux's terminal-features covers the other hop, tmux syncing its own redraws to the outer terminal. A commenter on the issue pointed out on April 14 that no released tmux supported the app side yet.

The tmux side had already been written. Chris Lloyd opened tmux PR #4744 on December 5, 2025: when a pane's app sends ESC[?2026h, tmux holds that pane's output until ESC[?2026l or a 1-second timeout, and drops the mode on resize so tmux can always redraw. The maintainer applied it to the OpenBSD tree, and the tmux CHANGES file lists it under 3.7, along with "Respond to DECRQM 2026".

The same author also wrote PR #5453 for xterm.js, the terminal component inside VS Code. The mode 2026 request, xterm.js issue #3375, was closed on December 20, 2025 by a commit titled "Add synchronized output support (DEC mode 2026)".

Issue #37283 itself was closed as not planned by a bot after going stale on May 28. The Claude Code docs now state the rule plainly: tmux releases through the 3.6 series don't implement synchronized output, so under them you may see more flicker than running Claude Code directly in your terminal. Upgrade to tmux 3.7 or later, or run Claude Code in its own tab. Check yours with tmux -V. The latest release is 3.8.

This script redraws a 20-row box 150 times. Each frame clears the screen, waits 10 ms to stand in for slow layout, then writes one row at a time with a flush after each. Pass sync and it wraps every frame in the mode 2026 pair.

#!/usr/bin/env python3 """ Redraw a box 150 times, with or without mode 2026. """ import sys import time sync = sys. argv [ 1:] == [ " sync " ] out = sys. stdout out. write ( " \x1b [?25l " ) # hide the cursor try: for n in range ( 150 ): if sync: out. write ( " \x1b [?2026h " ) # begin synchronized update out. write ( " \x1b [H \x1b [2J " ) # cursor home, erase screen out. flush () time. sleep ( 0.01 ) # pretend layout is slow for row in range ( 20 ): out. write ( f " | row { row: 2 } frame { n: 3 } | \n " ) out. flush () # one write per row if sync: out. write ( " \x1b [?2026l " ) # end: paint it all now out. flush () time. sleep ( 0.03 ) finally: out. write ( " \x1b [?2026l \x1b [?25h \n " ) # always end, show cursor Enter fullscreen mode Exit fullscreen mode Save it as flicker.py and use a window at least 22 rows tall. Run python3 flicker.py, then python3 flicker.py sync.

Then open tmux and run both again.

What to look for, from the spec rather than from my screen: without sync, the 10 ms blank sits between two writes, so the terminal can paint it and the box blinks. With sync on a terminal that supports the mode, the terminal keeps showing the previous full frame until the end marker, so only the frame counter should change. On a terminal or tmux that doesn't know the mode, both runs look the same, which is the same situation #37283 described. The finally line sends the end marker even if you press Ctrl+C mid-frame, so nothing stays frozen.

I build Hermes IDE, so weigh this section with that in mind. Hermes runs every agent's own interface in a real terminal, Claude Code included, the same way it would run anywhere else. It doesn't re-draw Claude Code's UI. The bytes Claude Code writes, frame markers and all, go to a terminal emulator, and Hermes renders its terminals GPU-accelerated with WebGL where the system supports it. The one exception is opt-in: tick "Agent view for Claude" on a session and Hermes renders the conversation itself instead of Claude's terminal interface.

Hermes 2.3.4: Codex and Claude Code side by side, each in its own terminal and hermes/ worktree, with status, context use, cost and branch in the sidebar.

That design means everything above applies inside Hermes too. Whether a frame tears depends on the terminal the agent writes to, so run the DECRQM probe and flicker.py in a Hermes shell next to your usual terminal and trust the reply over anyone's claim, mine included. If you start agents in tmux sessions from Hermes's New Session window, the tmux version rule from the Claude Code docs still holds.

Hermes runs on macOS, Windows and Linux and is source-available under BSL 1.1 on GitHub; you can download it here. It works the same way for every vendor, with your own sign-ins.

A 16 ms frame budget, and a 1-second timeout that gives up on a missing end marker instead of freezing the pane, are the same latency and timeout calls you make in any backend. My System Design Pocket Guide: Fundamentals covers those building blocks one at a time, without the interview theatre.

Quelle: dev.to (dev.to). Original öffnen