Documentation Index
Fetch the complete documentation index at: https://docs.monolex.ai/llms.txt
Use this file to discover all available pages before exploring further.
Simulation vs Presentation
This page explores a fundamental pattern shared between game engines and MonoTerm’s terminal architecture: separating the “truth” (simulation/state) from its “presentation” (rendering/display).The Core Insight
╔════════════════════════════════════════════════════════════════════════╗
║ ║
║ "THE WORLD DOESN'T STOP JUST BECAUSE YOU BLINKED" ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ In games: ║
║ * The ball keeps moving even if the screen can't show every position ║
║ * Physics simulation is the TRUTH ║
║ * Rendered frames are just SNAPSHOTS of that truth ║
║ ║
║ In terminals: ║
║ * Characters keep arriving even if the screen can't show every update ║
║ * Grid state is the TRUTH ║
║ * Screen updates are just SNAPSHOTS of that truth ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ THE PATTERN: ║
║ ║
║ ┌───────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ SIMULATION (Backend) PRESENTATION (Frontend) │ ║
║ │ ==================== ====================== │ ║
║ │ │ ║
║ │ * Runs continuously * Runs when possible │ ║
║ │ * Never skips * May skip/delay │ ║
║ │ * Is the "truth" * Shows the "truth" │ ║
║ │ * Deterministic * Best-effort │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────┘ ║
║ ║
╚════════════════════════════════════════════════════════════════════════╝
Game Engine Frame Management
The Fixed Timestep Pattern
╔════════════════════════════════════════════════════════════════════════╗
║ GAME ENGINE: PHYSICS vs RENDERING ║
╠════════════════════════════════════════════════════════════════════════╣
║ ║
║ TIME ───────────────────────────────────────────────────────────▶ ║
║ ║
║ PHYSICS (Fixed 60Hz - NEVER skips): ║
║ ║
║ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ║
║ │ P1 │ │ P2 │ │ P3 │ │ P4 │ │ P5 │ │ P6 │ │ P7 │ │ P8 │ │ P9 │ │P10 │ ║
║ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ ║
║ │ │ │ │ │ │ │ │ │ │ ║
║ v v v v v v v v v v ║
║ 16.6ms 16.6ms 16.6ms 16.6ms 16.6ms 16.6ms 16.6ms 16.6ms 16.6ms 16.6ms ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ RENDERING (Variable - MAY skip): ║
║ ║
║ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ║
║ │ R1 │ │ R2 │ │ R4 │ │ R7 │ │ R8 │ │R10 │ ║
║ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ ║
║ │ │ ^ │ ^ ^ │ │ ^ │ ║
║ │ │ │ │ │ │ │ │ │ │ ║
║ │ │ SKIP! │ SKIP! SKIP! │ │ SKIP! │ ║
║ │ │ (GPU │ (GPU busy) │ │ (GPU │ ║
║ │ │ busy) │ │ │ busy) │ ║
║ v v v v v v ║
║ ║
║ WHAT USER SEES: ║
║ ║
║ Frame 1 ──▶ Frame 2 ───────────▶ Frame 4 ────────────▶ Frame 7 ──▶ ...║
║ │ │ ║
║ Interpolated Interpolated ║
║ (smooth jump) (smooth jump) ║
║ ║
╚════════════════════════════════════════════════════════════════════════╝
The Spiral of Death
Why clamping is essential to prevent infinite loops.╔════════════════════════════════════════════════════════════════════════╗
║ THE SPIRAL OF DEATH: Why Clamping is Essential ║
╠════════════════════════════════════════════════════════════════════════╣
║ ║
║ WITHOUT CLAMP: ║
║ ║
║ Frame takes 200ms (huge lag spike) ║
║ │ ║
║ v ║
║ Need 12 physics steps to catch up! ║
║ │ ║
║ v ║
║ 12 steps take 100ms... ║
║ │ ║
║ v ║
║ Now still behind! ║
║ │ ║
║ v ║
║ INFINITE LOOP - GAME FREEZES ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ WITH CLAMP (max 250ms): ║
║ ║
║ Frame takes 500ms ║
║ │ ║
║ v ║
║ Clamp to 250ms ║
║ │ ║
║ v ║
║ 250ms of simulation SKIPPED (acknowledged loss) ║
║ │ ║
║ v ║
║ Game continues from new baseline ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ PHILOSOPHY: ║
║ ║
║ "It's better to lose some simulation time than to freeze forever" ║
║ ║
║ ┌───────────────────────────────────────────────────────────────┐ ║
║ │ RECOVERABLE vs UNRECOVERABLE │ ║
║ │ ──────────── ───────────── │ ║
║ │ Skip 250ms of physics Infinite freeze loop │ ║
║ │ User notices a "jump" Game is dead │ ║
║ │ Game continues Must force quit │ ║
║ └───────────────────────────────────────────────────────────────┘ ║
║ ║
╚════════════════════════════════════════════════════════════════════════╝
MonoTerm ACK Gate Pattern
The terminal equivalent of game frame management.╔═════════════════════════════════════════════════════════════════════════╗
║ MONOTERM: VTE PARSING vs GRID UPDATES ║
╠═════════════════════════════════════════════════════════════════════════╣
║ ║
║ TIME ───────────────────────────────────────────────────────────▶ ║
║ ║
║ VTE PARSING (Continuous - NEVER stops): ║
║ ║
║ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ║
║ │ V1 │ │ V2 │ │ V3 │ │ V4 │ │ V5 │ │ V6 │ │ V7 │ │ V8 │ │ V9 │ │V10 │ ║
║ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ ║
║ │ │ │ │ │ │ │ │ │ │ ║
║ v v v v v v v v v v ║
║ Parse Parse Parse Parse Parse Parse Parse Parse Parse Parse ║
║ "ls" "-la" "\n" "foo" "bar" ... ... ... ... ... ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ GRID UPDATES (ACK-gated - MAY wait): ║
║ ║
║ ┌────┐ ┌────┐ ░░░░░░ ░░░░░░ ░░░░░░ ┌────────────────────┐ ┌────┐ ┌────┐║
║ │ G1 │ │ G2 │ ░WAIT░ ░WAIT░ ░WAIT░ │ G3 (batched) │ │ G4 │ │ G5 │║
║ └────┘ └────┘ ░░░░░░ ░░░░░░ ░░░░░░ └────────────────────┘ └────┘ └────┘║
║ │ │ │ │ │ ║
║ │ │ ^ ^ ^ │ │ │ ║
║ │ │ │ │ │ │ │ │ ║
║ │ │ waiting_for_ack = true │ │ │ ║
║ │ │ (Frontend processing G2) │ │ │ ║
║ v v v v v ║
║ Send Send Send Send Send ║
║ │ │ ║
║ └────── ACK ──────────────────────────┘ ║
║ received! ║
║ ║
║ WHAT USER SEES: ║
║ ║
║ Update 1 ──▶ Update 2 ──────────────────▶ Update 3 ──▶ Update 4 ──▶... ║
║ │ ║
║ Batched ║
║ (contains V3,V4,V5,V6 changes) ║
║ ║
╚═════════════════════════════════════════════════════════════════════════╝
Side-by-Side Comparison
The Two Loops
╔═════════════════════════════════════════════════════════════════════════╗
║ GAME LOOP vs TERMINAL LOOP ║
╠═════════════════════════════════════════════════════════════════════════╣
║ ║
║ ┌───────────────────────────────────┐ ┌────────────────────────────────┐
║ │ GAME LOOP │ │ MONOTERM LOOP │
║ │ │ │ │
║ │ while (running) { │ │ while (session_active) { │
║ │ │ │ │
║ │ // Time management │ │ // Data management │
║ │ dt = get_delta_time(); │ │ data = pty.read(); │
║ │ accumulator += dt; │ │ grid.update(data); │
║ │ │ │ │
║ │ // Fixed timestep physics │ │ // Continuous VTE parsing │
║ │ while (acc >= FIXED_DT) { │ │ // (always runs) │
║ │ physics_step(FIXED_DT); │ │ │
║ │ acc -= FIXED_DT; │ │ │
║ │ } │ │ │
║ │ │ │ // ACK-gated sending │
║ │ // Render when possible │ │ if (!waiting_for_ack) { │
║ │ alpha = acc / FIXED_DT; │ │ send_grid_update(); │
║ │ render(interpolate(alpha)); │ │ waiting_for_ack = true; │
║ │ │ │ } else { │
║ │ │ │ has_pending = true; │
║ │ │ │ } │
║ │ } │ │ } │
║ │ │ │ │
║ └───────────────────────────────────┘ └────────────────────────────────┘
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ KEY INSIGHT: Both patterns solve PRODUCER-CONSUMER synchronization ║
║ ║
║ ┌───────────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ PRODUCER MEDIATOR CONSUMER │ ║
║ │ ──────── ──────── ──────── │ ║
║ │ │ ║
║ │ Game: │ ║
║ │ Physics ───────▶ Accumulator ───────▶ Renderer │ ║
║ │ (fixed) (time storage) (variable) │ ║
║ │ │ ║
║ │ MonoTerm: │ ║
║ │ VTE Parser ───────▶ Grid State ───────▶ xterm.js │ ║
║ │ (continuous) (cell storage) (ACK-gated) │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────────┘ ║
║ ║
╚═════════════════════════════════════════════════════════════════════════╝
Truth vs Presentation
╔════════════════════════════════════════════════════════════════════════╗
║ "SOURCE OF TRUTH" ARCHITECTURE ║
╠════════════════════════════════════════════════════════════════════════╣
║ ║
║ ║
║ ╔═══════════════════════════════════════════════════════════════╗ ║
║ ║ ║ ║
║ ║ SOURCE OF TRUTH ║ ║
║ ║ ================ ║ ║
║ ║ ║ ║
║ ║ Game: Physics State MonoTerm: Grid State ║ ║
║ ║ ┌───────────────────┐ ┌───────────────────┐ ║ ║
║ ║ │ ball.x = 150 │ │ cell[0] = 'A' │ ║ ║
║ ║ │ ball.y = 200 │ │ cell[1] = 'B' │ ║ ║
║ ║ │ ball.vx = 5 │ │ cursor = (5,10) │ ║ ║
║ ║ │ ball.vy = -3 │ │ fg = red │ ║ ║
║ ║ └───────────────────┘ └───────────────────┘ ║ ║
║ ║ │ │ ║ ║
║ ║ │ Always │ Always ║ ║
║ ║ │ up-to-date │ up-to-date ║ ║
║ ║ │ │ ║ ║
║ ╠═════════╪════════════════════════════╪════════════════════════╣ ║
║ │ │ ║
║ v v ║
║ ╔═══════════════════════════════════════════════════════════════╗ ║
║ ║ ║ ║
║ ║ PRESENTATION ║ ║
║ ║ ============ ║ ║
║ ║ ║ ║
║ ║ Game: Rendered Frame MonoTerm: xterm.js View ║ ║
║ ║ ┌───────────────────┐ ┌───────────────────┐ ║ ║
║ ║ │ o │ │ $ ls -la │ ║ ║
║ ║ │ \ │ │ foo bar baz │ ║ ║
║ ║ │ \ │ │ │ ║ ║
║ ║ │ * │ │ │ ║ ║
║ ║ └───────────────────┘ └───────────────────┘ ║ ║
║ ║ │ │ ║ ║
║ ║ │ May be │ May be ║ ║
║ ║ │ outdated │ outdated ║ ║
║ ║ │ │ ║ ║
║ ╚═══════════════════════════════════════════════════════════════╝ ║
║ ║
║ ║
║ THE KEY PRINCIPLE: ║
║ ┌───────────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ "The presentation is a SNAPSHOT of the truth, │ ║
║ │ not the truth itself. │ ║
║ │ │ ║
║ │ Missing a snapshot doesn't change the truth. │ ║
║ │ The truth continues regardless of whether we can show it." │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────────┘ ║
║ ║
╚════════════════════════════════════════════════════════════════════════╝
Skip vs Delay vs Batch
Three strategies for handling overwhelming data.╔════════════════════════════════════════════════════════════════════════╗
║ HANDLING BACKPRESSURE: THREE APPROACHES ║
╠════════════════════════════════════════════════════════════════════════╣
║ ║
║ INPUT: [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] ║
║ (10 updates, consumer can only handle 5) ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ STRATEGY 1: SKIP (Game Frames) ║
║ ║
║ OUTPUT: [A] [B] ─── ─── [E] [F] ─── ─── [I] [J] ║
║ ^ ^ ║
║ │ │ ║
║ C,D lost G,H lost ║
║ ║
║ OK Constant latency ║
║ OK Never falls behind ║
║ X Data permanently lost ║
║ X Requires interpolation for smoothness ║
║ ║
║ USE WHEN: Visual continuity matters more than exact accuracy ║
║ (games, video, animation) ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ STRATEGY 2: DELAY (Simple Queue) ║
║ ║
║ OUTPUT: [A] [B] [C] [D] [E] ... (eventually) ... [F] [G] [H] [I] [J] ║
║ ^ ║
║ │ ║
║ Growing delay ║
║ ║
║ OK No data loss ║
║ X Unbounded latency ║
║ X Can cause memory exhaustion ║
║ X User sees increasingly stale data ║
║ ║
║ USE WHEN: Every piece of data is critical and timing doesn't matter ║
║ (file transfers, logging) ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ STRATEGY 3: BATCH (MonoTerm ACK Gate) ║
║ ║
║ OUTPUT: [A] [B] [C+D+E+F] [G] [H+I+J] ║
║ ^ ^ ║
║ │ │ ║
║ Batched into Batched into ║
║ single update single update ║
║ ║
║ OK No data loss ║
║ OK Bounded latency (waits for ACK, not queue) ║
║ OK Efficient (fewer IPC calls) ║
║ OK Latest state always shown ║
║ ║
║ USE WHEN: All data matters AND latest state is most important ║
║ (terminals, real-time editors, collaborative tools) ║
║ ║
╚════════════════════════════════════════════════════════════════════════╝
Why Terminals Need Batching, Not Skipping
╔═════════════════════════════════════════════════════════════════════════╗
║ WHY TERMINALS CAN'T USE GAME-STYLE FRAME SKIPPING ║
╠═════════════════════════════════════════════════════════════════════════╣
║ ║
║ GAME: ║
║ ┌───────────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ Frame 1: Ball at (100, 100) │ ║
║ │ Frame 2: Ball at (110, 105) <── SKIPPED │ ║
║ │ Frame 3: Ball at (120, 110) <── SKIPPED │ ║
║ │ Frame 4: Ball at (130, 115) <── SHOWN │ ║
║ │ │ ║
║ │ User sees: Ball "jumps" from (100,100) to (130,115) │ ║
║ │ With interpolation: Smooth movement │ ║
║ │ │ ║
║ │ IS THIS OK? OK YES - position (110,105) was just intermediate │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────────┘ ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ TERMINAL: ║
║ ┌───────────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ Update 1: "$ ls" │ ║
║ │ Update 2: "\n" <── SKIPPED? │ ║
║ │ Update 3: "foo bar" <── SKIPPED? │ ║
║ │ Update 4: "\n$ " <── SHOWN │ ║
║ │ │ ║
║ │ User sees: "$ ls\n$ " │ ║
║ │ MISSING: "foo bar" (the actual output!) │ ║
║ │ │ ║
║ │ IS THIS OK? X NO - "foo bar" is CRITICAL information │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────────┘ ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ THE DIFFERENCE: ║
║ ║
║ ┌───────────────────────────────────┬────────────────────────────────┐ ║
║ │ GAME │ TERMINAL │ ║
║ ├───────────────────────────────────┼────────────────────────────────┤ ║
║ │ Each frame = position snapshot │ Each update = NEW information │ ║
║ │ Intermediate frames redundant │ Every character matters │ ║
║ │ Final position is what counts │ All characters must be shown │ ║
║ │ Can interpolate between states │ Cannot "interpolate" characters│ ║
║ └───────────────────────────────────┴────────────────────────────────┘ ║
║ ║
║ ════════════════════════════════════════════════════════════════════ ║
║ ║
║ MONOTERM SOLUTION: ║
║ ║
║ ┌───────────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ Batch updates, don't skip them │ ║
║ │ │ ║
║ │ Update 1: "$ ls" ──▶ SEND │ ║
║ │ Update 2: "\n" ] │ ║
║ │ Update 3: "foo bar" ]────▶ BATCH into single update │ ║
║ │ Update 4: "\n$ " ] │ ║
║ │ │ ║
║ │ User sees: Everything, just slightly delayed │ ║
║ │ Nothing is lost │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────────┘ ║
║ ║
╚═════════════════════════════════════════════════════════════════════════╝
The Universal Principle
╔══════════════════════════════════════════════════════════════════════════╗
║ ║
║ ╔═══════════════════════════════════════════════════════════════════╗ ║
║ ║ ║ ║
║ ║ THE SIMULATION-PRESENTATION SEPARATION PRINCIPLE ║ ║
║ ║ ║ ║
║ ║ ═══════════════════════════════════════════════════════════ ║ ║
║ ║ ║ ║
║ ║ 1. Simulation (Backend) owns the TRUTH ║ ║
║ ║ - Runs at its own pace ║ ║
║ ║ - Never stops for presentation ║ ║
║ ║ - State is always consistent ║ ║
║ ║ ║ ║
║ ║ 2. Presentation (Frontend) shows SNAPSHOTS ║ ║
║ ║ - Runs when resources allow ║ ║
║ ║ - May skip, delay, or batch ║ ║
║ ║ - Decoupled from simulation timing ║ ║
║ ║ ║ ║
║ ║ 3. A MEDIATOR absorbs timing differences ║ ║
║ ║ - Prevents producer from blocking ║ ║
║ ║ - Allows consumer to catch up ║ ║
║ ║ ║ ║
║ ║ 4. BACKPRESSURE prevents system collapse ║ ║
║ ║ - Acknowledges consumer capacity limits ║ ║
║ ║ - Gracefully degrades (skip/delay/batch) ║ ║
║ ║ - Prevents unbounded queue growth ║ ║
║ ║ ║ ║
║ ╚═══════════════════════════════════════════════════════════════════╝ ║
║ ║
║ ║
║ APPLICATIONS OF THIS PRINCIPLE: ║
║ ║
║ ┌───────────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ Domain │ Simulation │ Presentation │ Strategy │ ║
║ │ ────────────────┼─────────────────┼───────────────┼───────────── │ ║
║ │ Games │ Physics engine │ Renderer │ Skip+Interp │ ║
║ │ Video Streaming │ Source video │ Player │ Adaptive │ ║
║ │ Terminals │ VTE parser │ Display │ Batch │ ║
║ │ Real-time DBs │ Write log │ Query results │ Snapshot │ ║
║ │ Financial │ Order book │ Charts │ Throttle │ ║
║ │ IoT Sensors │ Measurements │ Dashboard │ Sample │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────────┘ ║
║ ║
╚══════════════════════════════════════════════════════════════════════════╝
Summary
╔════════════════════════════════════════════════════════════════════════╗
║ SUMMARY ║
╠════════════════════════════════════════════════════════════════════════╣
║ ║
║ Q: "Is it the same pattern - simulation continues, only frames drop?" ║
║ ║
║ A: YES. Exactly the same principle: ║
║ ║
║ ┌───────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ GAME: Physics runs ──▶ Render may skip ──▶ Interpolate │ ║
║ │ MONOTERM: VTE parses ──▶ Send may wait ──▶ Batch │ ║
║ │ │ ║
║ │ Both: Backend TRUTH never stops │ ║
║ │ Both: Frontend PRESENTATION adapts to capacity │ ║
║ │ Both: MEDIATOR absorbs timing mismatches │ ║
║ │ Both: BACKPRESSURE prevents system collapse │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────┘ ║
║ ║
║ KEY DIFFERENCE: ║
║ ║
║ ┌───────────────────────────────────────────────────────────────┐ ║
║ │ │ ║
║ │ Games SKIP frames (visual continuity, can interpolate) │ ║
║ │ Terminals BATCH updates (every character matters) │ ║
║ │ │ ║
║ └───────────────────────────────────────────────────────────────┘ ║
║ ║
║ THE MONOTERM PHILOSOPHY: ║
║ ║
║ "The PTY never stops. The VTE parser never stops. ║
║ Only the presentation waits for the consumer. ║
║ The truth continues, whether or not anyone is watching." ║
║ ║
╚════════════════════════════════════════════════════════════════════════╝