Skip to content

feat(@wterm/dom): add PredictiveEcho helper for mosh-style local echo#80

Open
vmxmy wants to merge 1 commit into
vercel-labs:mainfrom
vmxmy:feat/example-local-echo
Open

feat(@wterm/dom): add PredictiveEcho helper for mosh-style local echo#80
vmxmy wants to merge 1 commit into
vercel-labs:mainfrom
vmxmy:feat/example-local-echo

Conversation

@vmxmy
Copy link
Copy Markdown

@vmxmy vmxmy commented May 23, 2026

Summary

Add PredictiveEcho to @wterm/dom as an opt-in utility that gives a
terminal connected to a remote PTY the feeling of a local one — mosh
"client-side prediction" applied to the wterm/ghostty render pipeline.

No existing public API is changed. Consumers that don't import
PredictiveEcho are unaffected.

Why

When wterm is used to drive a remote PTY (cloudflared tunnel, SSH in
browser, cross-region WebSocket), the round trip between keystroke and
echo is visible — typing feels sluggish even on a fast network. Mosh
solves this with client-side prediction over its own SSP protocol.

We can't replicate mosh's protocol (we run raw PTY bytes over WebSocket
by design), but we can approximate the client-side half:

  • Paint printable ASCII to the local terminal immediately on keypress.
  • Track those characters in a FIFO queue.
  • When the server later echoes the same bytes, silently consume them
    from the queue (no duplicate glyphs).
  • When the server sends anything that doesn't match (escape sequences,
    mid-prompt redraws), roll back the queue with \x1b[ND\x1b[NX and let
    the server stream take over — the ghostty / wterm core remains the
    source of truth.
  • Predictions are disabled in alt-screen mode (vim, less, htop, claude
    code, ...) so full-screen applications render exactly as the server
    sent them.

In practice this turns a perceived ~100ms typing latency over a
Cloudflare Tunnel into something that feels local for typing-only flows,
without breaking anything app-level. Worst-case behaviour on a
Powerlevel10k prompt is a brief flicker as the server redraws the prompt
on top of the prediction (the rollback path) — acceptable trade-off and
in line with what mosh does on the same prompt.

Public API

import { WTerm, WebSocketTransport, PredictiveEcho } from "@wterm/dom";

const term = new WTerm(el);
const ws = new WebSocketTransport({ url: "wss://example.com/pty" });

const echo = new PredictiveEcho({
  term,
  send: (data) => ws.send(data),
});

term.onData = (data) => echo.handleInput(data);
ws.onData    = (data) => echo.handleServerData(data);

await term.init();
ws.connect();

PredictiveEchoOptions.shouldPredict?: (data, term) => boolean is the
escape hatch — return true for whatever you want to predict (default:
single printable ASCII byte, off in alt-screen).

Implementation notes

  • Single file packages/@wterm/dom/src/predictive-echo.ts, ~115 LOC.
  • Re-exported from packages/@wterm/dom/src/index.ts.
  • README example added below the existing WebSocketTransport section.
  • No new runtime dependencies.
  • Does not call into ghostty / wterm internals — only term.write(),
    term.bridge?.usingAltScreen(), and the caller's send callback.

Field tested

Deployed against the examples/local backend over a Cloudflare Tunnel
(QUIC) + Cloudflare Access, with ghostty as the core. Used daily for
Claude Code / zsh + Powerlevel10k / tmux / vim — typing feels native,
rollback flicker on prompt redraws is rare and brief.

Happy to add tests in __tests__/predictive-echo.test.ts if you want
this merged — let me know what kind of coverage you'd prefer (unit
against a fake WTerm, or behavior-level via a real WasmBridge).

PredictiveEcho is an opt-in utility for terminals connected to a remote PTY
where the round-trip latency is large enough to feel sluggish (cloudflared
tunnels, SSH-in-browser, cross-region WebSocket).

- Predicts printable ASCII into the local terminal at typing latency
- Reconciles with server output via a FIFO byte-match queue; matching
  bytes are silently consumed so there are no duplicate glyphs
- Any unmatched byte (escape sequence, prompt redraw) rolls back the
  prediction queue with \x1b[ND\x1b[NX and lets the server stream take
  over — the ghostty / wterm core remains the source of truth
- Disabled in alt-screen mode (vim, less, htop, claude, ...) so
  full-screen apps render exactly as the server sent them, with a custom
  shouldPredict() escape hatch

No public API changes; existing consumers are unaffected.

Tested against an examples/local backend deployed behind Cloudflare Tunnel
+ Cloudflare Access; round-trip ~80-150ms reduced to perceived typing
latency for ASCII characters in zsh.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 23, 2026

@vmxmy is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant