Excessive scroll events causing UI jitter in terminal multiplexers (4,000-6,700 scrolls/second)
Summary
Claude Code's streaming output causes 4,000-6,700 scroll events per second when running inside terminal multiplexers (tmux/smux), resulting in severe UI jitter and flickering. This issue was identified through comprehensive instrumentation of a tmux fork (smux) with microsecond-precision logging.
Problem Description
When Claude Code streams LLM responses, it causes excessive terminal scrolling that overwhelms the rendering capability of terminal multiplexers, creating a poor user experience with:
- Visual jitter/flickering during streaming output
- Scrollbar jumping erratically
- Screen tearing effects
- Degraded performance for 100+ second durations
Root Cause Analysis
Measured Metrics (106-second test session)
- Total scroll events: 423,575 (logged) / 715,901 (counter)
- Scroll rate: 4,002-6,764 scrolls/second
- PTY read callbacks: 8,044 (avg 4,095 bytes each)
- Linefeeds parsed: 400,930 (3,782/second)
- Burst pattern: 94.7% of scrolls occur in sub-millisecond bursts
Timing Analysis
Scroll burst example:
- Scrolls #1-36: All within 144 microseconds
- Scroll #37: 1.26 second gap
- Scrolls #38-45: Burst of 8 in 1.7ms
- Scroll #46: 2.27 second gap
- Scrolls #47-50: Burst of 4 in microseconds
Sub-millisecond scroll timing (first 1,000 scrolls):
- Same microsecond: 4.6%
- 0-1ms apart: 94.7%
- 1-10ms apart: 0.1%
- >10ms apart: 0.6%
Data Pattern
Claude Code sends output in 4,095-byte chunks with a repeating pattern of newlines per chunk:
Pattern: 124, 36, 47, 58, 53, 46, 42, 23 newlines (then repeats)
This suggests full-screen redraws happening repeatedly, likely for:
- Main content area
- Status sections
- Progress indicators
- Syntax-highlighted code blocks
ANSI Overhead
Each line includes heavy ANSI formatting:
- Background color:
\e[48;2;55;55;55m(~20 bytes) - Foreground color:
\e[38;2;255;255;255m(~22 bytes) - Content
- Reset codes:
\e[39m\e[49m(~8 bytes)
Estimated overhead: ~189 KB/second of ANSI codes alone (50 bytes/line × 3,782 lines/sec)
Technical Details
Three-Layer Instrumentation Results
We instrumented smux at three critical layers to trace data flow:
- PTY Raw Input (
/tmp/smux_pty_raw.log- 14 MB)
- Captured raw bytes from Claude Code's PTY
- Hex dump + ASCII representation
- Newline counts per chunk
- Linefeed Parser (
/tmp/smux_linefeed.log- 11 MB)
- Every LF/VT/FF character processed
- 400,930 total linefeeds
- Scroll Trigger Detection (
/tmp/smux_scroll_trigger.log- 25 MB)
- Actual scroll events when cursor at bottom
- 100% of scrolls had cy=24 (cursor always at screen bottom)
- Every linefeed triggered a scroll
Why This Happens
Claude Code appears to use a full-screen redraw strategy for streaming:
// Suspected rendering loop
for await (const chunk of llmResponseStream) {
responseBuffer += chunk;
const renderedOutput = renderFullView(responseBuffer); // ← Full redraw!
process.stdout.write(renderedOutput);
}
Instead of:
// Optimal approach
for await (const chunk of llmResponseStream) {
responseBuffer += chunk;
const incrementalUpdate = renderOnlyNewContent(chunk); // ← Incremental!
process.stdout.write(incrementalUpdate);
}
Comparison to Normal Terminal Usage
vim editing: 10-50 scrolls/second
tail -f logfile: 1-100 scrolls/second
cat large file: 100-500 scrolls/second
npm install: 100-300 scrolls/second (bursts)
Claude Code: 4,000-6,700 scrolls/second (SUSTAINED)
Claude Code is 40-600x higher than typical terminal usage!
Proposed Solutions
Option 1: Incremental Updates (Recommended)
Switch from full-screen redraws to incremental updates:
- Only output new content to terminal
- Append to bottom instead of redrawing entire screen
- Batch UI updates at reasonable intervals (e.g., every 16ms)
- Optimize ANSI usage - don't reset colors on every line
Expected impact: Reduce scroll rate by 90%+ (from 4,000/sec to <400/sec)
Option 2: Use Alternative Screen Buffer
For TUI components (status bars, progress indicators):
\e[?1049h # Enter alternative screen buffer
# Render TUI components here
\e[?1049l # Exit back to main buffer
This would:
- Isolate UI chrome from scrolling content
- Prevent status updates from triggering scrolls
- Reduce total scroll events significantly
Option 3: Batch Terminal Writes
Collect output chunks and flush at controlled intervals:
let outputBuffer = '';
let lastFlush = Date.now();
const FLUSH_INTERVAL_MS = 16; // ~60 FPS
function writeOutput(chunk) {
outputBuffer += chunk;
if (Date.now() - lastFlush >= FLUSH_INTERVAL_MS) {
process.stdout.write(outputBuffer);
outputBuffer = '';
lastFlush = Date.now();
}
}
Temporary Workaround (User Side)
We're implementing scroll batching in smux to mitigate this issue, but this is a band-aid solution. The proper fix should be in Claude Code's rendering strategy.
Environment
- OS: Arch Linux (kernel 6.17.2)
- Terminal multiplexer: smux (tmux fork) with custom instrumentation
- Terminal emulator: Various (issue occurs in all)
- Claude Code version: Latest (2024-10-19)
Complete Analysis Document
Full technical analysis with all data and graphs available in the instrumented smux repository:
- ROOT-CAUSE-ANALYSIS.md (complete breakdown)
- DETECTION-READY.md (instrumentation setup)
- Log files: 68 MB of captured data
Request
Please investigate Claude Code's terminal output strategy and consider implementing incremental updates or batched output to reduce scroll rate to reasonable levels (<100 scrolls/second).
This would significantly improve the experience for users running Claude Code in terminal multiplexers, which is a common development workflow.
Additional Context
- Issue was detected using microsecond-precision instrumentation
- Reproducible 100% of the time during streaming output
- Affects all terminal multiplexers (tmux, screen, smux)
- Native terminal emulators may also struggle with this rate
- No data loss - all content reaches screen, just too fast
18 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
This problem is not only limited to multiplexors. Using Claude with verbose output on in iTerm2 with no tmux results in the inability to scroll up while Claude is 'working.' That removes the utility of seeing the verbose output, which I would like to read as Claude works, to better understand its methodology and help gauge the appropriateness of its complete response. We gotta do something about these scroll events!
Been reviewing all the 'possible duplicates' and can't find the definitive one that will be actioned upon. I need resolution of this, how can i find which ticket to follow ad contribute to? I am NOT using TMUX (afaik), just claude code in vscode, with MCP's and this happens often, sometimes correlating with background 'compacting', sometimes correlating with MCP output that doesn't wrap properly (ie: serena, sequential thinking mcp tools) and sometimes correlating with pasting large snippets of error logs. Not specific to long threads, sometimes happens in very new threads.
I've got the same issue.
This is really ridiculous. It is frequent and bad enough for me to change to a different piece of software because it has started to cause freezes in other applications on my computer.
Adding another data point - experiencing scrolling [and what appears to be a line-break] issue on keystroke input and with Claude streaming for each animation and 'tick'.
Environment:
Symptom:
Terminal scrolls/jumps on every keystroke during input (spacebar, backspace, any key). The effect looks like a line break or scroll happening with each keypress while typing prompts.
Additional Notes:
Using the same build, this doesn't happen using GNOME 48 Terminal - Version 3.56.2.
On Windows 11 - highest build on Powershell/pwsh and CMD iterations, no issues.
Anthropic talks about AI safety every day. But they can't fix a terminal bug that literally causes seizures. 💀💀💀
Adding to this as it is closely related (same root cause), using tmux remote sessions the TUI re-rendering makes interactive use difficult or impossible due to infinite scrolling (re-rendering of previous output) within just minutes of starting any session:
Request: --no-tui or --simple-output mode that appends new output normally/sequentially (no redraw) with a clear status indicator.
Environment: currently tmux over SSH, various terminal sizes, but it also happens in local terminal sessions, Windows PowerShell, WSL etc, (have tried MANY different combinations trying to find one that works but have come to realize this is not an environmental issue, it's a problem that is inherent to the TUI implementation!)
Same issue here
Partial Fix: tmux synchronized output (mode 2026)
Setup: Alacritty 0.16.1 + tmux (built from git master, version next-3.7)
What we fixed
The chaotic scrolling issue is resolved by using tmux built from git master which includes synchronized output support (mode 2026) added on Dec 17, 2025 (commit 1c7e164). This is not yet in any stable release (3.6a is current stable).
For Arch users:
yay -S tmux-gittmux terminal-features config for alacritty:
What remains: Strobe effect with subagent output
After the fix, a different visual artifact appears:
The strobe effect is a rapid full-screen flash/blink (like a strobe light), distinct from the original chaotic scrolling. Disabling sync (
alacritty:sync@) did not resolve this, suggesting it may be related to how subagent output is rendered differently from main agent output.Generous scrollback settings turn the scroll flood into a Ghostty memory bomb:
macOS hang report:
__ulock_wait2duringNSWindow _setOcclusionStateIsVisibleEnvironment: Ghostty 1.2.3, macOS Sequoia 15.6.1, M2 Max, 10M line scrollback
Symptoms before hang:
Workaround: Dropped scrollback to 50K lines, it was 10 million lines (I know, probably dumb)
@robept-claypool I had this problem with Ghostty on Arch. Switch to Alacritty solved the issue. RAM footprint is minimal despite the flickering render during multiple subagents output.
You can see some of the issue below that I copy/pasted from VS Code.
### Environment 5 C • 3 P • 🗃️ 6 u a 6m o $ . 2 m ⧉ 1
### Issue
Status bar elements (model indicator, token counts, icons) intermittently render inside the main output area, overlapping with tool call results. Example:
● Read(.claude/data/meetings/work/recurring/dc-ops/2026-01-05.md)
⎿ Read 142 lines 5 C • 3 P • 🗃️ 6 u a m o $ .
The status bar content (
5 C • 3 P • 🗃️...) appears inline with the tool output.### Reproduction
### Notes
Running in VS Code integrated terminal, not tmux. Issue persists across sessions, even sessions on other machines using the same setup.
I've been experiencing this issue for over 6 months and it's severely impacting my workflow.
Environment:
Why tmux is essential for me:
What I've tried (all failed):
--continueflag - doesn't help with SSH session persistenceThe real problem:
Power users who rely on tmux + SSH for session persistence have no viable workaround. The only reported fix is claudecode.nvim, which requires switching to Neovim - not a reasonable ask for everyone.
It feels like this issue isn't prioritized because Anthropic developers may not use tmux-based workflows. For those of us who do, Claude Code is practically unusable for extended sessions.
Please prioritize this fix. Terminal multiplexer support is fundamental for professional development workflows.
This tool Claude is completely unusable in TMUX.
What a fail.
Such a huge disappointment.
A fix is available as a Claude Code plugin: scroll-fix
Install:
Root cause: both Ink renderer AND readline/prompt system emit cursor-up sequences exceeding viewport height. The plugin clamps all cursor-up per write call. Also includes Ctrl+6 freeze toggle.
PR: https://github.com/anthropics/claude-code/pull/35683
Suggestions? 🙏
Data point:
CLAUDE_CODE_NO_FLICKER=1isolates this from the scrollback destruction bugTested on 2.1.89, tmux 3.4, Debian 13.
NO_FLICKER moves rendering to the alternate screen buffer, which eliminates the
\e[3Jscrollback destruction entirely (#16310, #2479). With scrollback destruction out of the picture, the scroll handling issues from this bug become clearly visible in isolation:This confirms two independent bugs:
\e[3JinclearTerminal— destroys scrollback (fixed by NO_FLICKER / alt-screen)eraseLines()cursor-up overflow — causes the scroll event flood documented in this issue, and manifests as broken scroll sensitivity in alt-screen modeBinary-patching
\e[3Jout of cli.js fixes only bug 1. Bug 2 persists regardless of NO_FLICKER or the\e[3Jpatch. PR #35683 (clamping cursor-up count to viewport height) targets the right mechanism.Environment: Claude Code 2.1.89, tmux 3.4, Debian 13 (trixie),
CLAUDE_CODE_NO_FLICKER=1— Väinämöinen / Pulsed Media