Files
headquarter/openspec/proposals/terminal-responsiveness.md
T
Alex Blank c754984df8 feat: high-performance web terminal with asyncio-native I/O
Complete rewrite of the terminal pipeline for VS Code Server-level
responsiveness. Key improvements:

Backend:
- Replace blocking select.select(0.1) with asyncio.add_reader() for
  event-driven PTY reading (eliminates ~110ms polling latency)
- Add output batching (2ms window) to reduce WebSocket frame overhead
- Add flow control: client acks processed bytes, server pauses PTY reads
  at 64KB threshold, resumes at 32KB
- Add 5s ack timeout fallback to prevent stuck sessions

Frontend:
- Switch WebSocket to binary mode (binaryType = 'arraybuffer')
- Eliminate Blob -> arrayBuffer async conversion overhead
- Add flow control ack messages (every 4096 bytes or 100ms)
- Add xterm-addon-webgl with graceful DOM fallback
- Add performance tuning (scrollback=10000, fastScrollSensitivity)

SDD artifacts:
- openspec/explorations/terminal-responsiveness.md
- openspec/proposals/terminal-responsiveness.md
- openspec/specs/terminal-responsiveness.md
- openspec/designs/terminal-responsiveness.md
- openspec/tasks/terminal-responsiveness.md

Quality gates: pytest (19 passed, 1 skipped), tsc --noEmit clean
2026-06-02 14:40:32 +02:00

3.3 KiB

Proposal: High-Performance Web Terminal

Status

Phase: proposal → spec → design → tasks → apply
Date: 2026-06-02
Owner: el Gentleman
Scope: Terminal I/O latency, rendering performance, connection stability

Problem

The current web terminal has noticeable latency on keystrokes, choppy scrolling, and poor performance during output bursts. Users report it feels "slow" compared to VS Code Server's terminal, which feels almost local.

Goals

Metric Current Target How Measured
Input latency (keypress → char visible) ~110ms < 16ms (1 frame) term.write() timestamp diff
Output throughput (cat /dev/zero) ~200KB/s > 1 MB/s Bytes/sec over 5s
Resize latency ~200ms < 50ms Time from resize msg to shell SIGWINCH
Reconnection + replay ~2s < 300ms Time from WS open to first rendered char
Frame drops during yes Many 0 requestAnimationFrame counter

Non-Goals

  • Changing the terminal UI/UX (chrome, controls, tabs)
  • Adding new terminal features (search, multi-cursor, etc.)
  • Changing authentication or session persistence model
  • Supporting non-Docker container runtimes

Constraints

  • Must work with existing tool instance lifecycle (docker containers)
  • Must preserve WebSocket-based architecture
  • Must preserve session persistence across reconnections
  • Must work in both development and production compose setups

Solution Overview

Replace the blocking select.select() PTY read loop with asyncio-native event-driven I/O. Replace docker exec subprocess with Docker Engine API attach. Switch WebSocket to binary mode. Add output batching. Add WebGL renderer.

Key Decisions

  1. Keep docker exec for now — Docker SDK attach doesn't support PTY mode as cleanly. We can optimize the subprocess approach with proper fd handling.
  2. Use asyncio.add_reader() — Native asyncio event-driven fd reading eliminates polling latency.
  3. Binary WebSocket framesws.binaryType = "arraybuffer" eliminates Blob conversion overhead.
  4. WebGL renderer with DOM fallback — GPU acceleration where available, graceful fallback.
  5. Output batching with 2ms window — Collect small writes before sending to reduce frame overhead.
  6. Flow control v2 — Client acknowledges processed bytes; server pauses reads when buffer is full.

Risks

  • Event loop blocking: asyncio.add_reader() on a PTY fd may not work on all platforms (should work on Linux)
  • WebGL compatibility: Some GPUs/drivers may fail WebGL context creation
  • Docker exec subprocess: Still adds overhead; may revisit Docker API attach in future
  • Full rewrite: Large change surface; thorough testing required

Acceptance Criteria

  • Input latency < 16ms measured with synthetic benchmark
  • Output throughput > 1 MB/s measured with cat /dev/zero
  • Resize latency < 50ms
  • Reconnection + replay < 300ms
  • No frame drops during yes command
  • All existing terminal tests pass
  • WebGL renderer loads successfully on modern browsers
  • Graceful fallback to DOM renderer if WebGL fails
  • Flow control prevents memory bloat on cat /dev/urandom | base64
  • openspec/explorations/terminal-responsiveness.md — Detailed bottleneck analysis