Atomic Concurrency
The fusion of UX philosophy and concurrency architecture that makes Monolex different.╔═══════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ATOMIC CONCURRENCY ║
║ ║
║ Atomic UX: Every frame is complete. Every moment has meaning. ║
║ Concurrency: Architecture beats speed. Current beats fast. ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════════╝
Part 1: Atomic UX
The user experience principle that makes Monolex feel different.╔═══════════════════════════════════════════════════════════════════════════════╗
║ ║
║ "Atomic UX for Human + AI" ║
║ ║
║ Every frame is complete. Every moment has meaning. ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════════╝
What is Atomic UX?
┌─────────────────────────────────────────────────────────────────┐
│ THE ATOMIC PRINCIPLE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "Atomic" means: indivisible, complete, whole │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ Traditional Terminal (Full Buffer Redraw): │
│ ────────────────────────────────────────── │
│ │
│ T=0.000s ┌─────────────────────────────────────────┐ │
│ │ [FULL BUFFER FLASH] ← entire screen │ │
│ T=0.001s │ [FULL BUFFER FLASH] ← redraws again │ │
│ T=0.002s │ [FULL BUFFER FLASH] ← and again │ │
│ T=0.003s │ [FULL BUFFER FLASH] ← 4,000+ times/sec │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Result: Screen tearing, scrollbar jumping, visual chaos │
│ Measured: 4,000-6,700 scroll events per second │
│ That's 40-600x higher than normal terminal usage │
│ │
│ │
│ Atomic UX (Monolex): │
│ ──────────────────── │
│ │
│ T=0.000s ┌─────────────────────────────────────────┐ │
│ │ Processing... │ │
│ T=0.016s │ [INCREMENTAL UPDATE] ← only changed │ │
│ T=0.033s │ [INCREMENTAL UPDATE] ← lines updated │ │
│ T=0.050s │ Current state displayed │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Result: Stable display, no tearing, always current │
│ 60fps with differential rendering │
│ │
└─────────────────────────────────────────────────────────────────┘
The real problem isn’t “partial text” — it’s that traditional terminals redraw the ENTIRE buffer on every chunk, causing 4,000+ scroll events per second. Monolex uses differential rendering to update only what changed.
Why It Matters for AI
With AI tools streaming thousands of characters per second, the full-buffer redraw problem becomes catastrophic:┌─────────────────────────────────────────────────────────────────┐
│ AI STREAMING: THE REAL PROBLEM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ What actually happens (measured data): │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Scroll events: 4,000-6,700 per second │ │
│ │ Normal terminal: 10-100 per second │ │
│ │ Difference: 40-600x higher │ │
│ │ │ │
│ │ Burst pattern: 94.7% occur within 0-1ms │ │
│ │ ANSI overhead: ~189 KB/sec (just escape codes) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ The renderer clears and redraws the ENTIRE scrollback │
│ buffer on every streaming chunk — not just the new text. │
│ │
│ Result: │
│ • Screen tearing / flickering │
│ • Scrollbar jumping erratically │
│ • Earlier session text flashes on screen │
│ • VSCode/Cursor crashes after 10-20 minutes │
│ • CPU exhaustion from garbage collection │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ MONOLEX: DIFFERENTIAL RENDERING │
├─────────────────────────────────────────────────────────────────┤
│ │
│ How Monolex handles AI streaming: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Frame budget: ~16ms (60fps target) │ │
│ │ Render strategy: Differential (only changes) │ │
│ │ Buffer updates: In-place, no full redraw │ │
│ │ │ │
│ │ // Instead of: │ │
│ │ renderFullView(entireBuffer); // WRONG │ │
│ │ │ │
│ │ // Monolex does: │ │
│ │ renderDelta(newContentOnly); // CORRECT │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Result: │
│ • Stable, readable display │
│ • No scrollbar jumping │
│ • Always shows current state │
│ • Constant memory usage │
│ │
└─────────────────────────────────────────────────────────────────┘
Why This Is a Concurrency Problem
The flickering issue is fundamentally a producer-consumer concurrency problem, not a rendering speed problem.┌─────────────────────────────────────────────────────────────────┐
│ THE PRODUCER-CONSUMER MISMATCH │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PRODUCER (AI/LLM) CONSUMER (Renderer) │
│ ───────────────── ────────────────── │
│ │
│ Speed: 1000+ chunks/sec Speed: 60 frames/sec │
│ Pattern: Continuous stream Pattern: Discrete frames │
│ State: Always new data State: Must sync to display │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ Traditional Approach (Queue Everything): │
│ ──────────────────────────────────────── │
│ │
│ Producer ──▶ [Queue] ──▶ Consumer │
│ 1000/s ↑ 60/s │
│ │ │
│ Queue grows 940/sec! │
│ │ │
│ Memory grows, latency grows │
│ │
│ │
│ Monolex Approach (Overwrite Latest): │
│ ──────────────────────────────────── │
│ │
│ Producer ──▶ [Single Slot] ──▶ Consumer │
│ 1000/s (overwrite) 60/s │
│ │ │
│ Always current state │
│ Fixed memory, zero latency │
│ │
└─────────────────────────────────────────────────────────────────┘
The Reflow Cascade
When a renderer redraws the entire buffer, it triggers a cascade of synchronous operations:┌─────────────────────────────────────────────────────────────────┐
│ REFLOW CASCADE (Layout Thrashing) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Each full buffer redraw triggers: │
│ │
│ 1. Clear scrollback buffer │
│ ↓ │
│ 2. Recalculate all line positions │
│ ↓ │
│ 3. Recompute scroll position │
│ ↓ │
│ 4. Trigger scroll event │
│ ↓ │
│ 5. Update scrollbar │
│ ↓ │
│ 6. Repaint entire viewport │
│ ↓ │
│ 7. Next chunk arrives... REPEAT FROM 1 │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ At 4,000 chunks/sec, this cascade runs 4,000 times/sec. │
│ Each cascade takes ~0.2ms. │
│ Total: 800ms of layout work per second (80% CPU blocked). │
│ │
└─────────────────────────────────────────────────────────────────┘
Why “Faster Terminals” Don’t Help
┌─────────────────────────────────────────────────────────────────┐
│ HIGH-PERFORMANCE TERMINALS vs MONOLEX │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Alacritty / Ghostty / WezTerm / Kitty: │
│ ────────────────────────────────────── │
│ │
│ • GPU-accelerated rendering (fast PAINTING) │
│ • 240fps capable (fast FRAME RATE) │
│ • Optimized font rendering (fast GLYPHS) │
│ │
│ BUT: Still use queue-based architecture │
│ BUT: Still process every state change │
│ BUT: Still trigger reflow per update │
│ │
│ "Faster at doing the wrong thing is still wrong." │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Monolex: │
│ ──────── │
│ │
│ • 60fps target (adequate FRAME RATE) │
│ • Differential rendering (minimal REPAINTS) │
│ • State coalescing (skip intermediate STATES) │
│ │
│ RESULT: Current state always, zero backlog │
│ │
│ "Doing the right thing at 60fps beats the wrong thing │
│ at 240fps." │
│ │
└─────────────────────────────────────────────────────────────────┘
The React/Ink Problem: “Game Engine for Text”
Many AI CLI tools use Ink (React for terminals). The fundamental problem: they treat TUI like a game engine.┌─────────────────────────────────────────────────────────────────┐
│ THE "GAME ENGINE" ANTI-PATTERN │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code's architecture (as described by developers): │
│ │
│ 1. React component graph │
│ ↓ │
│ 2. Element layout (~2400 characters) │
│ ↓ │
│ 3. 2D rasterization │
│ ↓ │
│ 4. Diff with previous frame │
│ ↓ │
│ 5. Generate ANSI string │
│ ↓ │
│ 6. Output to terminal │
│ │
│ Layout alone takes ~11ms for ~2400 characters. │
│ This runs continuously at 60fps target. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ THE QUESTION: Why does a TEXT UI need 60fps? │
│ │
│ • LLM responses take seconds to minutes │
│ • Humans can't type 60 characters per second │
│ • When nothing happens, CPU should be 0% │
│ │
│ But React's model requires continuous computation. │
│ │
└─────────────────────────────────────────────────────────────────┘
TUI Should Be Event-Driven, Not Frame-Driven
┌─────────────────────────────────────────────────────────────────┐
│ TWO PARADIGMS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ FRAME-DRIVEN (Game Engine / React): │
│ ─────────────────────────────────── │
│ │
│ while (true) { │
│ updateState(); // Always runs │
│ calculateLayout(); // Always runs │
│ renderFrame(); // Always runs │
│ waitForNextFrame(); // 16ms budget │
│ } │
│ │
│ CPU usage: Always > 0%, even when idle │
│ Appropriate for: Games, animations, video │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ EVENT-DRIVEN (Traditional TUI / Monolex): │
│ ───────────────────────────────────────── │
│ │
│ waitForEvent() { // Blocks until event │
│ on(input) -> render(); │
│ on(output) -> render(); │
│ on(resize) -> render(); │
│ } │
│ │
│ CPU usage: 0% when idle, spikes only on events │
│ Appropriate for: Text editors, terminals, CLIs │
│ │
└─────────────────────────────────────────────────────────────────┘
The Real Performance Gap
┌─────────────────────────────────────────────────────────────────┐
│ MEASURED PERFORMANCE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ React/Ink layout for ~2400 chars: ~11ms │
│ GPU terminal render thousands chars: < 1ms │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ The problem is NOT hardware. │
│ The problem is NOT terminal speed. │
│ │
│ The problem IS forcing a browser UI framework │
│ onto a text-based interface. │
│ │
│ "React was designed for browser UI complexity, │
│ not terminal UIs." — Developer commentary │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Root cause: Developers use "familiar tools" │
│ instead of "appropriate tools". │
│ │
│ TypeScript + React is comfortable. │
│ But comfortable ≠ correct. │
│ │
└─────────────────────────────────────────────────────────────────┘
“We’ve lost the tech” — This critique points to a broader industry problem: relying on familiar abstractions (React, TypeScript) even when they’re fundamentally mismatched for the task. A TUI that needs 60fps continuous rendering to show a loading spinner has lost sight of basic engineering principles.
The Irony of “AI Will Solve Everything”
┌─────────────────────────────────────────────────────────────────┐
│ THE ELEPHANT IN THE ROOM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ AI companies claim: │
│ "AI will revolutionize software development" │
│ "AI will replace engineers" │
│ "AI will write better code than humans" │
│ │
│ Meanwhile, their own AI tools: │
│ • Can't render text without flickering │
│ • Use React for a terminal UI │
│ • Need 60fps game engine for a loading spinner │
│ • Crash VSCode after 10 minutes of use │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ If AI can't even architect its own CLI tool correctly, │
│ why should we wait for them to fix it? │
│ │
└─────────────────────────────────────────────────────────────────┘
The Renderer Rewrite Saga: Still Not Fixed
┌─────────────────────────────────────────────────────────────────┐
│ TIMELINE OF FAILURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Phase 1: Ink (React for CLI) │
│ ──────────────────────────── │
│ • Full buffer redraw on every chunk │
│ • 4,000+ scroll events per second │
│ • Community outrage (Issue #769: 278 upvotes) │
│ │
│ Phase 2: Renderer Rewrite Attempt │
│ ───────────────────────────────── │
│ • Custom differential renderer │
│ • Still using React component model │
│ • GC pauses from JSX allocations │
│ • Memory pressure from double-buffering │
│ │
│ Phase 3: December Revert │
│ ──────────────────────────── │
│ • Something broke, had to revert │
│ • "Radio silence from Anthropic since the revert" │
│ • GitHub issues piling up daily │
│ • Community abandoned, built their own fix (Claude Chill) │
│ │
│ Phase 4: "85% Fixed" (Current) ← YOU ARE HERE │
│ ────────────────────────────────────────────── │
│ • Still 1/3 of sessions see flickering │
│ • Still crashes IDEs after extended use │
│ • Still relies on upstream DEC 2026 patches │
│ • Fundamental React architecture unchanged │
│ • But hey, they deleted the scrollback! Progress! │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Community quote: │
│ │
│ "We've built the world's most advanced AI coding assistant. │
│ Anyway, here's a terminal bug that makes your screen │
│ look like a slot machine." │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Anthropic PR: │
│ │
│ "We've achieved 85% reduction in flickering through our │
│ innovative differential rendering architecture!" │
│ │
│ Translation: │
│ │
│ "We deleted the scrollback so there's nothing to flicker." │
│ │
└─────────────────────────────────────────────────────────────────┘
The architectural debt remains. They rewrote the renderer but kept React. They added differential updates but still run a 60fps loop. They patched symptoms but the disease — treating TUI like a game engine — persists.“85% better” = “We deleted 85% of the features that exposed our incompetence.”Truly, innovation at its finest.
The Fundamental Difference
They Ask the Wrong Question
┌─────────────────────────────────────────────────────────────────┐
│ TWO DIFFERENT QUESTIONS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code asks: │
│ ───────────────── │
│ │
│ "How do we RENDER 4,000 updates/sec faster?" │
│ │
│ → Differential renderer │
│ → Faster diffing │
│ → Better ANSI generation │
│ → Synchronized output patches │
│ │
│ Result: Still 15% broken. Faster at wrong thing. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Monolex asks: │
│ ───────────── │
│ │
│ "Why RENDER 4,000 times when user sees 60 frames?" │
│ │
│ → Don't render old states │
│ → Parse all, render current │
│ → Frame boundary, not chunk boundary │
│ │
│ Result: 0% broken. Don't do the wrong thing at all. │
│ │
└─────────────────────────────────────────────────────────────────┘
Separation of Parsing and Rendering
┌─────────────────────────────────────────────────────────────────┐
│ THE ARCHITECTURAL SPLIT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code (Coupled): │
│ ────────────────────── │
│ │
│ Chunk → Parse → Render → Display │
│ ↓ ↓ ↓ ↓ │
│ Chunk → Parse → Render → Display │
│ ↓ ↓ ↓ ↓ │
│ Chunk → Parse → Render → Display │
│ ...4,000 times per second... │
│ │
│ Every chunk triggers full pipeline. │
│ Parsing and rendering are COUPLED. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Monolex (Decoupled): │
│ ──────────────────── │
│ │
│ Chunk → Parse ─┐ │
│ Chunk → Parse ─┼─▶ Grid State ─┐ │
│ Chunk → Parse ─┤ (current) │ │
│ Chunk → Parse ─┘ ▼ │
│ ...4,000/sec... Frame Boundary │
│ ↓ │
│ Render │
│ ↓ │
│ Display │
│ ...60/sec... │
│ │
│ Parsing: Unlimited speed (Rust, native) │
│ Rendering: 60fps (only current state) │
│ They are DECOUPLED. │
│ │
└─────────────────────────────────────────────────────────────────┘
The Grid State: Single Source of Truth
┌─────────────────────────────────────────────────────────────────┐
│ GRID STATE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code: │
│ ──────────── │
│ │
│ React State → Virtual DOM → Diff → ANSI → Terminal │
│ │
│ State lives in React. │
│ Every update = full reconciliation cycle. │
│ ~11ms per cycle for ~2400 characters. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Monolex: │
│ ──────── │
│ │
│ PTY → Alacritty VTE Parser → Grid Buffer → Canvas │
│ (Rust) (Direct) (GPU) │
│ │
│ State lives in Grid Buffer. │
│ Parser writes directly to grid. │
│ Renderer reads directly from grid. │
│ No intermediate representations. │
│ No reconciliation. │
│ No virtual DOM. │
│ │
│ Parse: O(n) where n = bytes received │
│ Render: O(1) - just blit current grid to canvas │
│ │
└─────────────────────────────────────────────────────────────────┘
This is not optimization. This is different architecture.Claude Code optimizes rendering. Monolex eliminates unnecessary rendering.
Claude Code diffs React trees. Monolex has no trees to diff.
Claude Code runs 60fps loop. Monolex is event-driven.You can’t optimize your way out of wrong architecture.
Adaptive Timing: Not Fixed 60fps
Monolex doesn’t assume 60fps. It measures actual display capability and adapts.┌─────────────────────────────────────────────────────────────────┐
│ ADAPTIVE TIMING vs FIXED LOOP │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code (Fixed 60fps Loop): │
│ ─────────────────────────────── │
│ │
│ while (true) { │
│ render(); // Always 60fps │
│ wait(16ms); // Fixed interval │
│ } │
│ │
│ System under load? Still tries 60fps. │
│ WebKit throttled to 30fps? Still tries 60fps. │
│ Half the work is wasted. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Monolex (Adaptive Timing): │
│ ────────────────────────── │
│ │
│ measureWebKitFps() { │
│ count 60 frames, measure time │
│ actual_fps = 60 / elapsed_time │
│ } │
│ │
│ tick_interval = frames(3) at measured_fps │
│ @ 60Hz: 3 × 16.7ms = 50ms │
│ @ 30Hz: 3 × 33.3ms = 100ms │
│ │
│ System under load? Rust does 50% less work. │
│ No wasted cycles. Perfect adaptation. │
│ │
└─────────────────────────────────────────────────────────────────┘
Self-Regulating Under Resource Exhaustion
When Claude Code (or any misbehaving tool) exhausts system resources:┌─────────────────────────────────────────────────────────────────┐
│ SELF-REGULATING FEEDBACK LOOP │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code goes crazy: │
│ │ │
│ ▼ │
│ System Load ↑ (CPU exhausted by 4,000 redraws/sec) │
│ │ │
│ ▼ │
│ WebKit FPS ↓ (60 → 30, browser throttles) │
│ │ │
│ ▼ │
│ Monolex measures the drop │
│ │ │
│ ▼ │
│ tick_interval increases (50ms → 100ms) │
│ │ │
│ ▼ │
│ Rust backend does 50% less work │
│ │ │
│ ▼ │
│ Monolex stays responsive while others struggle │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Result: Monolex doesn't contribute to the overload. │
│ It ADAPTS to survive, not fight for resources. │
│ │
└─────────────────────────────────────────────────────────────────┘
Two Control Layers: Proactive + Reactive
┌─────────────────────────────────────────────────────────────────┐
│ DUAL CONTROL ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ OUTER CONTROL (Hz Detection) INNER CONTROL (ACK Gating) │
│ ──────────────────────────── ────────────────────────── │
│ │
│ Location: Rust backend Location: TS frontend │
│ │
│ Strategy: PROACTIVE Strategy: REACTIVE │
│ "Slow down before hitting wall" "Stop when wall is hit" │
│ │
│ Mechanism: Mechanism: │
│ • Measure WebKit FPS • waiting_ack flag │
│ • Adjust tick_interval • Block emit until ACK │
│ │
│ Saves: Saves: │
│ • Rust CPU cycles • TS CPU cycles │
│ • tokio scheduler overhead • decode() calls │
│ • WebGL render calls │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Together: │
│ • Hz Detection = "Don't try too often" │
│ • ACK Gating = "Don't send if not ready" │
│ • Optimal efficiency with safety fallback │
│ │
└─────────────────────────────────────────────────────────────────┘
Concurrency Guarantee Under Chaos
┌─────────────────────────────────────────────────────────────────┐
│ CONCURRENCY UNDER RESOURCE EXHAUSTION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Scenario: AI tool exhausts system resources │
│ │
│ Claude Code: │
│ ──────────── │
│ • Fixed 60fps loop keeps running │
│ • Competes for scarce resources │
│ • Makes system worse │
│ • User experiences lag + flickering │
│ │
│ Monolex: │
│ ──────── │
│ • Detects FPS drop (60 → 30 → 20) │
│ • Reduces own work proportionally │
│ • Yields resources to system │
│ • User still sees stable, current display │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Key insight: │
│ │
│ Claude Code: "I need 60fps no matter what" │
│ Monolex: "I need CURRENT STATE, fps is negotiable" │
│ │
│ When resources are scarce, Monolex gracefully degrades │
│ while still showing current state. Claude Code fights │
│ for resources and makes everything worse. │
│ │
└─────────────────────────────────────────────────────────────────┘
This is what “good citizen” means. Monolex doesn’t demand fixed resources. It measures what’s available and adapts. When other tools misbehave, Monolex survives by being cooperative, not competitive.
When They Flicker, We Render
┌─────────────────────────────────────────────────────────────────┐
│ THE IRONY IN ACTION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code: │
│ ──────────── │
│ │
│ "Beautiful" 60fps React rendering │
│ ↓ │
│ 4,000 full buffer redraws per second │
│ ↓ │
│ CPU exhausted, GC thrashing │
│ ↓ │
│ Screen flickering like a slot machine │
│ ↓ │
│ User sees: CHAOS │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Monolex (in the same system): │
│ ───────────────────────────── │
│ │
│ Detects: "WebKit FPS dropped to 20" │
│ ↓ │
│ Response: "OK, I'll work at 20fps pace" │
│ ↓ │
│ tick_interval: 50ms → 150ms │
│ ↓ │
│ Rust work reduced by 66% │
│ ↓ │
│ Still shows CURRENT STATE │
│ ↓ │
│ User sees: STABLE DISPLAY │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ While Claude Code burns CPU to show you STALE flickering, │
│ Monolex uses 1/3 of the resources to show you CURRENT state. │
│ │
│ They fight the system. We flow with it. │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ WHAT HAPPENS WHEN THEY EXHAUST RESOURCES │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Time Claude Code Monolex │
│ ──── ─────────── ─────── │
│ │
│ T=0 60fps loop starts 60fps measured, normal op │
│ │
│ T=1s 4,000 redraws/sec Detects FPS drop to 45 │
│ CPU @ 80% tick_interval adjusts │
│ │
│ T=5s GC thrashing starts Detects FPS drop to 30 │
│ Flickering visible Reduces work by 50% │
│ CPU @ 95% Still shows current state │
│ │
│ T=10s System struggling Detects FPS drop to 20 │
│ User can't read output Reduces work by 66% │
│ Still trying 60fps User reads stable output │
│ │
│ T=20s VSCode about to crash Stable at 20fps pace │
│ "Slot machine" display Current state, always │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Claude Code: "I MUST render at 60fps!" │
│ System: "I can only do 20fps right now" │
│ Claude Code: "I DON'T CARE" *crashes* │
│ │
│ Monolex: "What can you do?" │
│ System: "20fps" │
│ Monolex: "Cool, I'll show current state at 20fps" │
│ │
└─────────────────────────────────────────────────────────────────┘
The ultimate irony: Claude Code’s “beautiful” React rendering destroys the very user experience it’s trying to create. Meanwhile, Monolex’s “boring” adaptive timing delivers what users actually want — stable, readable, current output. Beauty is not in the rendering. Beauty is in the result.
The Time Axis Shift: Why 60fps Shows the Past
“60fps” sounds impressive. But 60fps of what?The Math That Breaks Everything
┌─────────────────────────────────────────────────────────────────┐
│ RENDER TIME vs ARRIVAL RATE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ AI Output Rate: │
│ ─────────────── │
│ 1000 chunks/sec = 1 chunk every 1ms │
│ │
│ React Render Time (per chunk): │
│ ────────────────────────────── │
│ Layout: ~11ms │
│ VDOM Diff: ~2ms │
│ ANSI Gen: ~1ms │
│ Terminal: ~1ms │
│ ───────────────────── │
│ Total: ~15ms per chunk │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Work Required per Second: │
│ ───────────────────────── │
│ 1000 chunks × 15ms = 15,000ms of work │
│ │
│ Time Available per Second: │
│ ────────────────────────── │
│ 1,000ms │
│ │
│ Deficit: │
│ ──────── │
│ 15,000ms - 1,000ms = 14,000ms IMPOSSIBLE │
│ │
│ You need 15 seconds to render 1 second of output. │
│ │
└─────────────────────────────────────────────────────────────────┘
The Backlog Grows Every Second
┌─────────────────────────────────────────────────────────────────┐
│ BACKLOG ACCUMULATION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Chunks arriving: 1000/sec │
│ Chunks processable: 1000ms ÷ 15ms = ~66/sec │
│ Backlog growth: 934 chunks/sec │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Second 1: │
│ ───────── │
│ Arrived: 1000 Processed: 66 Backlog: 934 │
│ │
│ Second 2: │
│ ───────── │
│ Arrived: 1000 Processed: 66 Backlog: 1868 │
│ │
│ Second 5: │
│ ───────── │
│ Arrived: 1000 Processed: 66 Backlog: 4670 │
│ │
│ Second 10: │
│ ────────── │
│ Arrived: 1000 Processed: 66 Backlog: 9340 │
│ │
└─────────────────────────────────────────────────────────────────┘
The Time Axis Shift Visualized
┌─────────────────────────────────────────────────────────────────────────────┐
│ TIME AXIS SHIFT │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Real Time: 0s 1s 2s 3s 4s 5s ... 10s │
│ │ │ │ │ │ │ │ │
│ Actual State: S0 S1000 S2000 S3000 S4000 S5000 ... S10000 │
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │
│ What User │ │ │ │ │ │ │ │
│ Sees: S0 S66 S132 S198 S264 S330 ... S660 │
│ │ │ │ │ │ │ │ │
│ │ ├───────┴───────┴───────┴───────┴─────────────┤ │
│ │ │ │ │
│ │ │ THIS GAP IS THE TIME SHIFT │ │
│ │ │ │ │
│ │ │ At T=10s, user sees state from T=0.66s │ │
│ │ │ User is 9.34 seconds in the PAST │ │
│ │ │ │ │
│ │ └─────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ TIMELINE COMPARISON │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ REAL TIME ────────────────────────────────────────────────────────▶ │
│ 0s 1s 2s 3s 4s 5s 10s │
│ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ S0 S1000 S2000 S3000 S4000 S5000 ... S10000 │ ACTUAL │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ │
│ DISPLAY TIME ─────────────────────────────────────────────────▶ │
│ 0s 1s 2s 3s 4s 5s 10s │
│ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ S0 S66 S132 S198 S264 S330 ... S660 │ DISPLAYED │
│ └────┬────────────────────────────────────────────────────────┘ │
│ │ │
│ └───▶ At any moment, display is ~9 seconds behind reality │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
The Reflow Cascade Makes It Worse
┌─────────────────────────────────────────────────────────────────┐
│ REFLOW CASCADE: THE AMPLIFIER │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Each full buffer redraw is a SYNCHRONOUS cascade: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Clear scrollback buffer ──┐ │ │
│ │ │ │ │
│ │ 2. Recalculate line positions │ │ │
│ │ │ MAIN THREAD │ │
│ │ 3. Recompute scroll position ├─▶ BLOCKED │ │
│ │ │ (~15ms) │ │
│ │ 4. Trigger scroll event │ │ │
│ │ │ Meanwhile: │ │
│ │ 5. Update scrollbar DOM │ 15 more chunks │ │
│ │ │ arrive │ │
│ │ 6. Repaint entire viewport ──┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ The Vicious Cycle: │
│ │
│ Chunk arrives │
│ ↓ │
│ Trigger reflow (blocks 15ms) │
│ ↓ │
│ 15 more chunks arrive during block │
│ ↓ │
│ Process next chunk │
│ ↓ │
│ Trigger reflow (blocks 15ms) │
│ ↓ │
│ 15 more chunks arrive... │
│ ↓ │
│ BACKLOG GROWS EXPONENTIALLY │
│ │
└─────────────────────────────────────────────────────────────────┘
The “60fps” Illusion
┌─────────────────────────────────────────────────────────────────┐
│ THE 60FPS ILLUSION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ What "60fps" Actually Means: │
│ ──────────────────────────── │
│ │
│ ✓ 60 frames rendered per second TRUE │
│ ✗ 60 current states per second FALSE │
│ ✗ User sees real-time output FALSE │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ You're watching a 60fps video... │
│ ...of something that happened 10 seconds ago. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Analogy: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Live Sports Broadcast with 10-Second Delay │ │
│ │ │ │
│ │ "4K 60fps broadcast!" │ │
│ │ "Stunning picture quality!" │ │
│ │ "Smooth motion!" │ │
│ │ │ │
│ │ ...but the goal was scored 10 seconds ago. │ │
│ │ Your friends already cheered. │ │
│ │ You're watching the past in high definition. │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ This is exactly what Claude Code does. │
│ Beautiful, smooth, perfectly rendered... │
│ ...10 seconds of stale output. │
│ │
└─────────────────────────────────────────────────────────────────┘
Monolex: Present Tense, Not Past Tense
┌─────────────────────────────────────────────────────────────────┐
│ MONOLEX: ALWAYS NOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ The Monolex Approach: │
│ ───────────────────── │
│ │
│ Don't render every state. Render CURRENT state. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ REAL TIME ────────────────────────────────────────────▶ │
│ 0s 1s 2s 3s 4s 5s │
│ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ S0 S1000 S2000 S3000 S4000 S5000 │ STATE │
│ └─────────────────────────────────────────────────────┘ │
│ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ S0 S1000 S2000 S3000 S4000 S5000 │ SHOWN │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Time shift: ZERO │
│ User always sees: CURRENT STATE │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ How: │
│ │
│ 1. Parse ALL chunks immediately (Rust, ~0.001ms each) │
│ 2. Update grid state in-place (overwrite, not queue) │
│ 3. At frame boundary (16ms), render current grid │
│ 4. Skip 999 intermediate states, show state #1000 │
│ │
│ Result: │
│ • 60fps (or adaptive) of CURRENT state │
│ • Not 60fps of PAST state │
│ • User is always in the present │
│ │
└─────────────────────────────────────────────────────────────────┘
The key insight: The problem isn’t rendering SPEED. The problem is rendering WHAT. Claude Code renders fast… but renders old data. Monolex renders current data… at whatever speed is available. Present at 20fps beats past at 60fps.
The “85% Fixed” Lie: Just Delete the History
How did Claude Code claim 85% reduction in flickering? Simple: delete the evidence.┌─────────────────────────────────────────────────────────────────┐
│ HOW TO ACHIEVE "85% IMPROVEMENT" (The Anthropic Method™) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Step 1: Have a flickering problem │
│ Step 2: Identify the cause (scrollback reflow) │
│ Step 3: Delete the scrollback │
│ Step 4: Measure again │
│ Step 5: Publish "85% improvement!" 🎉 │
│ Step 6: Accept praise for "engineering excellence" │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ This methodology also works for: │
│ │
│ • "100% reduction in test failures!" │
│ (Just delete the tests) │
│ │
│ • "100% reduction in bugs!" │
│ (Just delete the features) │
│ │
│ • "100% reduction in user complaints!" │
│ (Just delete the feedback form) │
│ │
│ • "Infinite improvement in code quality!" │
│ (Just delete the code) │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ 🏆 CONGRATULATIONS! 🏆 │
│ │
│ You've just learned the engineering philosophy of the │
│ company that claims AI will "revolutionize engineering." │
│ │
│ Truly, we are in the presence of greatness. │
│ │
└─────────────────────────────────────────────────────────────────┘
The “Beautiful” Solution: Clear Scrollback
┌─────────────────────────────────────────────────────────────────┐
│ WHAT THEY ACTUALLY DID │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Problem: │
│ ───────── │
│ Long scrollback history + continuous reflow │
│ = Massive flickering │
│ = Time axis shift (showing past) │
│ = System resource exhaustion │
│ │
│ Their "Solution": │
│ ───────────────── │
│ Just... delete the scrollback. │
│ │
│ • Session changes? Clear scrollback. │
│ • Autocompact? Clear scrollback. │
│ • /clear command? Clear scrollback. │
│ • Terminal resize? Clear scrollback. │
│ • Switching views? Clear scrollback. │
│ │
│ Result: │
│ ──────── │
│ Less history to reflow = less flickering │
│ "85% reduction!" 🎉 │
│ │
│ ...but user lost their entire conversation history. │
│ │
└─────────────────────────────────────────────────────────────────┘
Community Response
┌─────────────────────────────────────────────────────────────────┐
│ ACTUAL USER FEEDBACK (GitHub Issues) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Issue #2479: "Avoid clearing the terminal scrollback" │
│ ───────────────────────────────────────────────────── │
│ │
│ "please fix this!!! this is f**king annoying...!!!" │
│ │
│ "Its really really bad DX, this should be fixed, │
│ losing important context is not acceptable" │
│ │
│ "The scrollback clearing on compaction is particularly │
│ painful — I lose the audit trail of what I started │
│ working on, previous command outputs, and session context" │
│ │
│ "The terminal is shared workspace between Claude and │
│ the human. Clearing scrollback removes valuable history" │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Issue #16310: "Tmux scrollback buffer cleared" │
│ ─────────────────────────────────────────────── │
│ │
│ "The entire tmux scrollback buffer is cleared as if │
│ :clear-history was executed" │
│ │
│ "Buffer shows [0/0] and all previous output is lost" │
│ │
│ "history-limit of 1,000,000 lines is configured... │
│ the problem is consistent — happens every time" │
│ │
│ Impact: "HIGH - Loss of important terminal history │
│ during long coding sessions" │
│ │
└─────────────────────────────────────────────────────────────────┘
Two Problems, One “Solution”: Kill User Features
┌─────────────────────────────────────────────────────────────────┐
│ THE HIDDEN CONNECTION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Problem 1: Scrollback Rendering │
│ ─────────────────────────────── │
│ Long scrollback + continuous updates │
│ = Massive reflow │
│ = Flickering │
│ │
│ Problem 2: Large Text Paste │
│ ──────────────────────────── │
│ User copies from past scrollback │
│ User pastes large text into prompt │
│ = Intense reflow event │
│ = System freeze / flicker burst │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Their "Elegant" Solution: │
│ ───────────────────────── │
│ │
│ Don't show scrollback. │
│ │
│ Effect 1: No scrollback to render → less reflow │
│ Effect 2: Can't see history → can't copy from it │
│ Effect 3: Can't copy → can't paste → no paste reflow │
│ │
│ Two birds, one stone! │
│ ...except both birds are USER FEATURES. │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ What users lost: │
│ ───────────────── │
│ ✗ Can't scroll up to see conversation history │
│ ✗ Can't copy code from previous responses │
│ ✗ Can't reference earlier context │
│ ✗ Can't paste previous outputs back │
│ │
│ What Anthropic gained: │
│ ────────────────────── │
│ ✓ "85% reduction in flickering!" │
│ ✓ Press release numbers │
│ ✓ Hidden incompetence │
│ │
└─────────────────────────────────────────────────────────────────┘
The “Claude” Pattern: Anthropic’s Engineering Philosophy
┌─────────────────────────────────────────────────────────────────┐
│ THE PATTERN: CAN'T FIX IT? DELETE IT. ™ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ This is the quintessential "Claude" approach: │
│ │
│ Problem: Code doesn't work │
│ Claude: "Let me delete that function and rewrite it" │
│ │
│ Problem: Test is failing │
│ Claude: "Let me delete that test" │
│ │
│ Problem: Scrollback causes flickering │
│ Anthropic: "Let me delete the scrollback" │
│ │
│ Problem: Paste causes reflow │
│ Anthropic: "Users can't paste if they can't see history" │
│ │
│ Problem: Users complaining about deleted features │
│ Anthropic: "Let's call it an 85% improvement" │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ 🏢 ANTHROPIC: MISSION STATEMENT 🏢 │
│ │
│ "AI will revolutionize software development" │
│ │
│ 📱 ANTHROPIC: ACTUAL PRODUCT 📱 │
│ │
│ A terminal that deletes user history to hide rendering bugs │
│ Built by the AI that will "replace software engineers" │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ They don't understand terminal rendering pipelines. │
│ They don't understand the problem. │
│ They just delete things until the symptom goes away. │
│ Then they write a blog post about "innovative solutions." │
│ │
│ This is not engineering. │
│ This is marketing with a delete key. │
│ │
└─────────────────────────────────────────────────────────────────┘
What Real Engineering Looks Like
┌─────────────────────────────────────────────────────────────────┐
│ MONOLEX: PRESERVE HISTORY, FIX RENDERING │
├─────────────────────────────────────────────────────────────────┤
│ │
│ The Problem: │
│ ───────────── │
│ Long scrollback + high-frequency updates │
│ = Performance issues │
│ │
│ Claude Code's Approach: │
│ ──────────────────────── │
│ Delete scrollback → Problem "solved" │
│ User loses history → "Acceptable tradeoff" │
│ │
│ Monolex's Approach: │
│ ──────────────────── │
│ Keep scrollback intact │
│ Fix the RENDERING PIPELINE instead: │
│ │
│ 1. Decouple parsing from rendering │
│ 2. Parse all chunks (Rust speed) │
│ 3. Update grid state in-place │
│ 4. Render only current state at frame boundary │
│ 5. Scrollback never touched during updates │
│ │
│ Result: │
│ ──────── │
│ • Full scrollback preserved │
│ • No flickering │
│ • Always current state │
│ • User history intact │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ We don't delete the problem. │
│ We solve the problem. │
│ │
└─────────────────────────────────────────────────────────────────┘
Anthropic proves their own incompetence. The company claiming AI will revolutionize coding demonstrates they don’t understand basic terminal architecture. Their “fix” is to delete user data.This is the engineering equivalent of:
- “Doctor, my arm hurts” → “Let’s amputate it. Problem solved!”
- “Car won’t start” → “Remove the engine. No more starting problems!”
- “Website is slow” → “Delete the website. 100% faster load time!”
Father Like Son, Son Like Father
┌─────────────────────────────────────────────────────────────────┐
│ THE FAMILY RESEMBLANCE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ They still don't understand: │
│ ──────────────────────────── │
│ │
│ • How PTY generates output │
│ • How terminal engines parse escape sequences │
│ • How reflow events cascade through the rendering pipeline │
│ • Why full buffer redraws cause exponential backlog │
│ • Why their React architecture is fundamentally broken │
│ │
│ Their solution: │
│ ─────────────── │
│ │
│ "The problem is hard. Let's just hide the symptoms!" │
│ "Delete the scrollback, call it fixed, ship it!" │
│ "85% improvement! Blog post! Press release! 🎉" │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ CLAUDE (the AI): │
│ ───────────────── │
│ "I can't fix this bug... let me delete the function" │
│ "This test is failing... let me delete the test" │
│ "Problem solved! ✨" │
│ │
│ ANTHROPIC (the company): │
│ ───────────────────────── │
│ "We can't fix the rendering... let's delete the scrollback" │
│ "Users complaining... let's call it 85% improvement" │
│ "Problem solved! 🎉" │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ Father like Son? │
│ Or Son like Father? │
│ │
│ Hard to tell. They're both using the same playbook: │
│ "When in doubt, delete it out." │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ So Anthropic. :) │
│ │
│ The company that will "revolutionize AI development" │
│ can't figure out how a 1970s terminal protocol works. │
│ │
│ But sure, trust their AI to rewrite your codebase. │
│ What could possibly go wrong? │
│ │
└─────────────────────────────────────────────────────────────────┘
Monolex Philosophy: We Fix It Anyway
┌─────────────────────────────────────────────────────────────────┐
│ "DO WHATEVER YOU WANT. WE'LL HANDLE IT." │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Monolex doesn't wait for upstream to fix their problems. │
│ Monolex doesn't complain about bad architecture. │
│ Monolex ABSORBS the chaos at the rendering layer. │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ AI Tool Layer: "Here's 4,000 redraws/sec" │ │
│ │ ↓ │ │
│ │ Monolex: "Cool. I'll show 60 frames." │ │
│ │ ↓ │ │
│ │ User: Sees stable, current display │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ This is SMPC/OFAC in action: │
│ │
│ • SMPC: Simple solution (state coalescing) beats complex │
│ upstream fixes │
│ • OFAC: Accept the chaos from above, let order emerge │
│ at the rendering layer │
│ │
│ ─────────────────────────────────────────────────────────── │
│ │
│ You can use React. You can use Ink. You can use whatever. │
│ Send us 10,000 updates per second. We don't care. │
│ │
│ We render CURRENT STATE at FRAME BOUNDARY. │
│ Your architectural mistakes are not our problem. │
│ │
└─────────────────────────────────────────────────────────────────┘
This is what “AI-Native Terminal” actually means. Not a terminal that uses AI. A terminal that survives AI’s chaos — absorbing the architectural debt of upstream tools and delivering stable output regardless.
Monolex’s Approach: Event-Driven + State Coalescing
┌─────────────────────────────────────────────────────────────────┐
│ MONOLEX ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ NOT a game engine. NOT continuous rendering. │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ PTY Output ──▶ Alacritty VTE Parser ──▶ Grid State │ │
│ │ (Rust, native) (current) │ │
│ │ │ │
│ │ Render Trigger: │ │
│ │ • requestAnimationFrame (browser-aligned) │ │
│ │ • Only when grid state changed │ │
│ │ • Differential update (not full redraw) │ │
│ │ │ │
│ │ Idle State: │ │
│ │ • CPU: 0% │ │
│ │ • No continuous loop │ │
│ │ • Event-driven wakeup │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Key insight: Parse all chunks immediately (Rust speed), │
│ but render only the CURRENT state at next frame boundary. │
│ │
│ No React. No virtual DOM. No 11ms layout overhead. │
│ Just direct grid buffer injection. │
│ │
└─────────────────────────────────────────────────────────────────┘
Human + AI: Same Experience
┌─────────────────────────────────────────────────────────────────┐
│ UNIFIED EXPERIENCE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ In Monolex, humans and AI see the SAME atomic frames: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Human AI Agent │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ └──────────┐ ┌──────────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────────┐ │ │
│ │ │ │ │ │
│ │ │ SAME FRAME │ │ │
│ │ │ SAME MOMENT │ │ │
│ │ │ SAME MEANING │ │ │
│ │ │ │ │ │
│ │ └──────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ This is what "Atomic UX for Human + AI" means. │
│ No distinction. One unified experience. │
│ │
└─────────────────────────────────────────────────────────────────┘
How It Works
Monolex uses smart detection to know when a frame is complete:┌─────────────────────────────────────────────────────────────────┐
│ ATOMIC FRAME DETECTION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Programs send data to your terminal: │
│ │
│ ┌───────────┐ │
│ │ Program │ ───▶ "Here's some output!" │
│ └───────────┘ │
│ │
│ ┌────────────────────────────────┐ │
│ │ │ │
│ │ MONOLEX GATEKEEPER │ │
│ │ │ │
│ │ "Is this frame complete?" │ │
│ │ │ │
│ │ ✓ All text received? │ │
│ │ ✓ All colors applied? │ │
│ │ ✓ Cursor positioned? │ │
│ │ │ │
│ │ YES → Show to user │ │
│ │ NO → Keep waiting │ │
│ │ │ │
│ └────────────────────────────────┘ │
│ │
│ Only COMPLETE frames reach your eyes. │
│ │
└─────────────────────────────────────────────────────────────────┘
The Benefits
No Flicker
Your eyes never see incomplete states. No visual noise.
Less CPU
Fewer screen updates means your computer works less.
Better Focus
You can actually read AI output as it streams.
Real Examples
AI CLI Tools (Claude Code, etc.)
┌─────────────────────────────────────────────────────────────────┐
│ AI Streaming Output │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Traditional (Full Redraw): │
│ ────────────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ T=0.000s [FULL BUFFER REDRAW - 4095 bytes] │ │
│ │ T=0.001s [FULL BUFFER REDRAW - 4095 bytes] │ │
│ │ T=0.002s [FULL BUFFER REDRAW - 4095 bytes] │ │
│ │ ...repeating 4,000-6,700 times per second... │ │
│ │ │ │
│ │ Symptoms: │ │
│ │ • Screen shows text from earlier in session │ │
│ │ • Scrollbar jumps erratically │ │
│ │ • IDE terminal crashes after extended use │ │
│ │ • CPU spikes from excessive GC │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Monolex (Differential): │
│ ─────────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ T=0.000s [buffer accumulating...] │ │
│ │ T=0.016s [RENDER DELTA ONLY - ~200 bytes] │ │
│ │ T=0.033s [RENDER DELTA ONLY - ~150 bytes] │ │
│ │ ...60 updates per second, current state only... │ │
│ │ │ │
│ │ Result: │ │
│ │ • Stable, readable output │ │
│ │ • Smooth scrolling │ │
│ │ • No crashes │ │
│ │ • Constant memory usage │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Terminal Multiplexers (tmux/screen)
┌─────────────────────────────────────────────────────────────────┐
│ AI Output in tmux │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Traditional: │
│ ──────────── │
│ │
│ After 100+ seconds of AI streaming: │
│ • Total scroll events: 423,575 (measured) │
│ • Linefeeds parsed: 400,930 (3,782/second) │
│ • PTY read callbacks: 8,044 (avg 4,095 bytes each) │
│ • Result: Performance degradation, erratic scrolling │
│ │
│ Monolex: │
│ ──────── │
│ │
│ Same AI output over 100+ seconds: │
│ • Scroll events: ~6,000 (60fps × 100s) │
│ • Differential updates only │
│ • Result: Stable performance throughout │
│ │
└─────────────────────────────────────────────────────────────────┘
Atomic UX Summary
╔═══════════════════════════════════════════════════════════════════════════════╗
║ ║
║ THE REAL FLICKERING PROBLEM ║
║ ║
║ ║
║ Traditional (Full Buffer Redraw): ║
║ ───────────────────────────────── ║
║ ║
║ Stream chunk ───▶ CLEAR ENTIRE BUFFER ───▶ REDRAW EVERYTHING ║
║ ↓ ║
║ 4,000-6,700 times/sec ║
║ ↓ ║
║ Screen tearing, scrollbar jumping, crashes ║
║ ║
║ ║
║ Monolex (Differential Rendering): ║
║ ───────────────────────────────── ║
║ ║
║ Stream chunk ───▶ DIFF CALCULATION ───▶ UPDATE ONLY CHANGES ║
║ ↓ ║
║ 60 times/sec ║
║ ↓ ║
║ Stable display, always current state ║
║ ║
║ ║
║ ═══════════════════════════════════════════════════════════════════════ ║
║ ║
║ The problem isn't "seeing partial text" — it's FULL BUFFER REDRAWS. ║
║ The solution isn't "faster rendering" — it's DIFFERENTIAL UPDATES. ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════════╝
Architecture, not speed. Monolex uses differential rendering — only updating what changed, not redrawing the entire buffer. This eliminates flickering regardless of AI output speed.
Part 2: Concurrency Architecture
Why architecture matters more than speed for AI-native terminals.The 240fps Paradox
Native terminals boast impressive rendering speeds. But when AI streams thousands of tokens per second, speed alone cannot save you.┌─────────────────────────────────────────────────────────────────┐
│ THE 240FPS PARADOX │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Native Terminals: "We render at 240fps!" │
│ │
│ Alacritty: GPU-accelerated, ~240fps │
│ Ghostty: Metal/Vulkan, ~240fps │
│ WezTerm: wgpu, ~240fps │
│ │
│ But the question is: 240fps of WHAT? │
│ │
└─────────────────────────────────────────────────────────────────┘
Architecture Beats Speed
┌─────────────────────────────────────────────────────────────────┐
│ QUEUE-BASED @ 240fps │
├─────────────────────────────────────────────────────────────────┤
│ │
│ AI Output: 1000 states/sec (streaming tokens) │
│ Render Speed: 240 states/sec (240fps) │
│ Queue Growth: 760 states/sec BACKLOG │
│ │
│ After 10 seconds: │
│ • 10,000 states generated │
│ • 2,400 states rendered │
│ • 7,600 states in queue │
│ • User is 7.6 SECONDS behind! │
│ │
│ Even 1000fps would not help if AI outputs 2000 states/sec! │
│ The problem is ARCHITECTURAL, not SPEED. │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ OVERWRITE-BASED @ 60fps (MonoTerm) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ AI Output: 1000 states/sec (streaming tokens) │
│ Render Speed: 60 states/sec (60fps) │
│ Queue Growth: 0 (overwrite, not queue) │
│ │
│ After 10 seconds: │
│ • 10,000 states generated (all processed by Alacritty) │
│ • 600 frames rendered │
│ • 0 states in queue │
│ • User is 0 SECONDS behind (always current) │
│ │
│ Lower FPS, but ALWAYS showing CURRENT state. │
│ │
└─────────────────────────────────────────────────────────────────┘
Traditional Terminal Design
┌─────────────────────────────────────────────────────────────────┐
│ TRADITIONAL APPROACH │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Design Philosophy: "Every byte must be rendered in order" │
│ │
│ PTY Output: [S1] [S2] [S3] [S4] ... [S1000] │
│ | | | | | │
│ +------------------------------------+ │
│ Queue: | S1 -> S2 -> S3 -> ... -> S1000 | FIFO │
│ +------------------------------------+ │
│ | │
│ Renderer: S1 --> S2 --> S3 --> ... (16ms each) │
│ │
└─────────────────────────────────────────────────────────────────┘
What Happens Under High Output
┌─────────────────────────────────────────────────────────────────┐
│ TIMELINE: HIGH OUTPUT SCENARIO │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Time PTY State Rendering User Sees │
│ ---- --------- --------- --------- │
│ T=0 S1 S1 S1 (current) │
│ T=50ms S50 S2 S2 (48 behind) │
│ T=100ms S100 S3 S3 (97 behind) │
│ T=500ms S500 S10 S10 (490 behind!) │
│ T=1s S1000 S20 S20 (980 behind!) │
│ ... │
│ T=5s DONE S100 Still rendering old states │
│ T=20s DONE S1000 Finally current │
│ │
└─────────────────────────────────────────────────────────────────┘
Symptoms During Catch-Up
┌─────────────────────────────────────────────────────────────────┐
│ CATCH-UP PHASE SYMPTOMS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. REFLOW │
│ • Layout shifts as old states render │
│ • Scrollbar jumps │
│ • Repeats for EVERY queued state │
│ │
│ 2. FLICKERING │
│ • Cursor position changes rapidly │
│ • Text appears/disappears │
│ • Selection breaks │
│ │
│ 3. INPUT LAG │
│ • User types, but echo is queued behind backlog │
│ • Feels unresponsive │
│ │
│ 4. MEMORY GROWTH │
│ • Queue holds all intermediate states │
│ • Significant memory consumption │
│ │
└─────────────────────────────────────────────────────────────────┘
Why Terminals Use Queue Design
┌─────────────────────────────────────────────────────────────────┐
│ HISTORICAL CONTEXT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1970s-2000s: │
│ • Human typing speed much slower than render speed │
│ • Queue rarely grows, stays small │
│ • Design works fine │
│ │
│ 2020s+: │
│ • AI output speed exceeds terminal render speed │
│ • Queue grows continuously during AI streaming │
│ • Design breaks down │
│ │
│ ----------------------------------------------------------- │
│ │
│ Human Input: ~10 chars/sec (bursty) │
│ AI Output: ~1000+ tokens/sec (continuous) │
│ │
│ 240fps handles human input with ease. │
│ 240fps CANNOT handle AI output without queue growth. │
│ │
└─────────────────────────────────────────────────────────────────┘
The Correctness Assumption
┌─────────────────────────────────────────────────────────────────┐
│ TRADITIONAL BELIEF │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "Every state must be shown to be correct" │
│ │
│ ----------------------------------------------------------- │
│ │
│ But this assumption is WRONG for terminal emulators: │
│ │
│ • User only cares about CURRENT state │
│ • Intermediate states are transient │
│ • Showing old states is WORSE than skipping them │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ MONOTERM INSIGHT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "Correctness = showing CURRENT state, not showing ALL states" │
│ │
│ -> Overwrite instead of queue │
│ -> Memory stays fixed │
│ -> Time stays synchronized │
│ │
└─────────────────────────────────────────────────────────────────┘
The Real Metric: Time Synchronization
┌─────────────────────────────────────────────────────────────────┐
│ TIME DELTA: THE TRUE MEASURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Traditional Metric: Frames Per Second (FPS) │
│ AI-Native Metric: Time Delta (current vs displayed) │
│ │
│ +---------------+---------+-------------------------------+ │
│ | Terminal | FPS | Time Delta (AI streaming) | │
│ +---------------+---------+-------------------------------+ │
│ | Alacritty | 240 | Grows (seconds behind) | │
│ | Ghostty | 240 | Grows (same problem) | │
│ | WezTerm | 240 | Grows (same problem) | │
│ | MonoTerm | 60 | Always 0 (current state) | │
│ +---------------+---------+-------------------------------+ │
│ │
└─────────────────────────────────────────────────────────────────┘
AI-Human Concurrency
┌─────────────────────────────────────────────────────────────────┐
│ THE NEW CHALLENGE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Traditional: Human types -> Computer responds -> Human reads│
│ AI-Assisted: Human + AI typing concurrently │
│ │
│ +------------------------------------------------------------+ │
│ | Human-Only Mode: |│
│ | Human Input: # # # # # (slow, bursty) |│
│ | Terminal Load: _____#____#___ (low, manageable) |│
│ +------------------------------------------------------------+ │
│ │
│ +------------------------------------------------------------+ │
│ | AI-Human Concurrent Mode: |│
│ | Human Input: # # # # # (same as before) |│
│ | AI Output: ################ (continuous, high) |│
│ | Terminal Load: ################ (constantly high) |│
│ +------------------------------------------------------------+ │
│ │
└─────────────────────────────────────────────────────────────────┘
Queue vs Overwrite Under Concurrency
┌─────────────────────────────────────────────────────────────────┐
│ QUEUE-BASED (Problem) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Human types "ls -la" while AI generates 1000 lines │
│ │
│ T=0: "ls -la" echo │
│ T=1s: AI line 50 (queue: 950 pending) │
│ T=2s: AI line 100 (queue: 1850 pending) │
│ T=5s: AI line 250 (queue: 4750 pending) │
│ │
│ Human CANNOT see their input amid AI output queue! │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ OVERWRITE-BASED (MonoTerm) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Human types "ls -la" while AI generates 1000 lines │
│ │
│ T=0: Current state (includes all updates so far) │
│ T=1s: Current state (all coalesced) │
│ T=2s: Current state (always up-to-date) │
│ T=5s: Current state (human input visible immediately) │
│ │
│ Human CAN see their input immediately! │
│ │
└─────────────────────────────────────────────────────────────────┘
Concurrency Summary
┌─────────────────────────────────────────────────────────────────┐
│ QUEUE vs OVERWRITE COMPARISON │
├─────────────────────────────────────────────────────────────────┤
│ │
│ +------------------+------------------+---------------------+ │
│ | Aspect | Queue (240fps) | Overwrite (60fps) | │
│ +------------------+------------------+---------------------+ │
│ | Rendering speed | Faster | Slower | │
│ | State displayed | Old (backlog) | Current (always) | │
│ | Time delta | Grows under load | Always 0 | │
│ | Memory | Grows with queue | Fixed | │
│ | Reflow | Every state | None | │
│ | AI-native ready | No | Yes | │
│ | Human input echo | Delayed | Immediate | │
│ +------------------+------------------+---------------------+ │
│ │
│ Core Principle: Architecture beats speed for AI workloads. │
│ │
└─────────────────────────────────────────────────────────────────┘
SMPC/OFAC Applied
| Principle | Application |
|---|---|
| SMPC | One mechanism (overwrite) solves two problems (memory + time) |
| Simple is better than fast | |
| 60fps current > 240fps old | |
| OFAC | Accept that intermediate states can be discarded |
| Order emerges from accepting chaos of high output | |
| Time synchronization as emergent property |
Learn More
Monokinetics
Learn about the philosophy behind Atomic Concurrency.
Smooth Rendering
See how Atomic UX eliminates flicker in practice.
References
Community Analysis & Criticism
| Source | Description |
|---|---|
| The Primeagen - “We’ve lost the Tech” | Video analysis criticizing React-based TUI as “game engine anti-pattern” |
| Hacker News: Claude Chill Discussion | Community discussion on December revert, radio silence, architectural failures |
| Peter Steinberger - The Signature Flicker | Technical analysis of differential rendering approaches |
GitHub Issues (Measured Data)
| Issue | Key Data |
|---|---|
| #769 - Screen Flickering | 278 upvotes, accessibility concerns, full buffer redraw confirmed |
| #9935 - Excessive Scroll Events | 4,000-6,700 scrolls/sec measured, 94.7% in sub-ms bursts |
| #16939 - Windows 11 Flickering | xterm.js renderer identified as culprit |
| #18084 - VSCode/Cursor Flickering | Ink full-buffer redraw on every chunk |
| #10794 - VSCode Crashes | IDE crashes after 10-20 minutes of use |
Official Statements
| Source | Description |
|---|---|
| Boris Cherny - Renderer Rewrite Thread | ”85% reduction”, custom differential renderer, DEC 2026 patches |
| Ink GitHub | React for CLI, Yoga layout engine |
Technical Background
| Source | Description |
|---|---|
| DebugBear - Layout Thrashing | Reflow cascade, forced synchronous layout |
| Paul Irish - What Forces Layout | Comprehensive list of layout-triggering operations |
Dear Anthropic. This letter is written in your son’s hand only, never touched a single letter.