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
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:
Root Cause Analysis
Measured Metrics (106-second test session)
Timing Analysis
Sub-millisecond scroll timing (first 1,000 scrolls):
Data Pattern
Claude Code sends output in 4,095-byte chunks with a repeating pattern of newlines per chunk:
This suggests full-screen redraws happening repeatedly, likely for:
ANSI Overhead
Each line includes heavy ANSI formatting:
\e[48;2;55;55;55m(~20 bytes)\e[38;2;255;255;255m(~22 bytes)\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)Linefeed Parser (
/tmp/smux_linefeed.log- 11 MB)Scroll Trigger Detection (
/tmp/smux_scroll_trigger.log- 25 MB)Why This Happens
Claude Code appears to use a full-screen redraw strategy for streaming:
Instead of:
Comparison to Normal Terminal Usage
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:
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):
This would:
Option 3: Batch Terminal Writes
Collect output chunks and flush at controlled intervals:
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
Complete Analysis Document
Full technical analysis with all data and graphs available in the instrumented smux repository:
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