Skip to Content
OperationsPerformance HUD

Performance HUD

The Performance HUD is an in-app overlay that shows the runtime’s view of itself: frame time, diff size, output bytes, and the current degradation tier. It is designed to be cheap to render, deterministic, and safe to leave on during normal use — so you can reach for it any time you suspect a regression without worrying that the overlay itself is distorting the measurement.

Source: crates/ftui-demo-showcase/src/app.rs (HUD widget + JSONL emitter) and crates/ftui-widgets/ (observability widgets).

When it is useful

  • After a refactor. You changed something in the rendering path; the HUD confirms the p99 didn’t creep.
  • During a UX tune. Gradients look nice but cost bytes; the HUD shows exactly how many.
  • While reproducing a bug. A user says “the inline log viewer feels stuttery”; turning on the HUD tells you which tier you’re in and whether the cascade is flapping.
  • Before opening a PR. A quick sanity check that your branch still hits the SLO budgets.

Quick start

Run the demo showcase on the Performance screen and toggle the HUD:

cargo run -p ftui-demo-showcase -- --screen=10

Keybindings inside the demo:

KeyEffect
Ctrl+PToggle the Performance HUD.
?Toggle the help overlay.
F12Toggle the debug overlay (strategy internals, not just HUD).
Ctrl+KOpen the command palette — cmd:toggle_perf_hud works from there too.
Tab / Shift+TabCycle screens.
q, Ctrl+CQuit.

The demo script

scripts/perf_hud_demo.sh wraps the above with convenient defaults.

./scripts/perf_hud_demo.sh # Alt-screen, defaults ./scripts/perf_hud_demo.sh --inline # Inline mode ./scripts/perf_hud_demo.sh --inline --ui-height 12 ./scripts/perf_hud_demo.sh --auto-exit 1500 # Quit after 1.5 s ./scripts/perf_hud_demo.sh --pty # Wrap in a PTY (useful in CI) ./scripts/perf_hud_demo.sh --no-mouse # Disable mouse (cleaner captures)

All flags pass through to ftui-demo-showcase. See E2E scripts for the full E2E-gate version (scripts/perf_hud_e2e.sh).

What the HUD shows

The HUD is built from the observability widget family. A compact layout at the top-right corner surfaces:

RowMeaning
Tick / FPSObserved tick rate and frames per second over the rolling window.
Frame timep50 / p95 / p99 in microseconds over the last few hundred frames.
BudgetRemaining time in the current frame’s SLO budget.
TierCurrent degradation tierFull, SimpleBorders, NoColors, TextOnly.
DiffNumber of changed cells + run count for the last diff.
OutputBytes emitted this frame + bytes per changed cell.

On a very small terminal the HUD falls back to a single-line minimal view: FPS 58 p99 3.4ms tier=Full diff=42c.

Emitting JSONL from the HUD

Every HUD update can also be tee’d to an append-only JSONL file — the same file CI replays use — by setting:

FTUI_PERF_HUD_JSONL=/tmp/perf_hud.jsonl cargo run -p ftui-demo-showcase -- --screen=10

Schema:

{"schema_version":"perf-hud-v1","run_id":"…","seq":N,"event":"perf_hud","frame_time_p99_us":3412,"tier":"Full","diff_cells":42,"output_bytes":2180}

Two emit functions inside app.rs (emit_perf_hud_jsonl for string fields and emit_perf_hud_jsonl_numeric for numeric ones) produce these lines. They share the monotonic sequence counter used by the evidence sink, so lines from the HUD and lines from the runtime interleave cleanly.

Tick stall detection

The HUD’s emitter also includes a stall watchdog:

  • TICK_STALL_WARN_AFTER = 750 ms. If no ticks are observed for longer than this, a warning is logged.
  • TICK_STALL_LOG_INTERVAL = 2000 ms. Subsequent stall warnings are rate-limited to one every 2 s to avoid drowning the ledger.

A stall warning usually means one of: an update took too long, a subscription died, or the clock source changed (see determinism fixtures).

Determinism guarantees

The HUD is part of the kernel’s determinism contract:

  • Output is deterministic given identical inputs. The same input sequence produces the same HUD text at the same frames, under E2E_DETERMINISTIC=1.
  • Missing data renders as n/a, never panics. If the rolling window hasn’t accumulated enough samples to produce a p99, the HUD shows p99 n/a — a deliberate rendering, not a crash.
  • Tiny terminals fall back to a single-line view; the overlay is never truncated into garbage.

Troubleshooting

The HUD isn’t appearing. Press Ctrl+P, or open the command palette with Ctrl+K and run cmd:toggle_perf_hud. If keybindings don’t land, your terminal may be swallowing Ctrl+P — try the command palette route.

The HUD overlaps my widgets. Increase your terminal size, or switch to inline mode (./scripts/perf_hud_demo.sh --inline --ui-height 18).

The HUD says n/a for p99. Not enough samples yet. Let the app render for a few seconds, or generate activity (scroll a list, resize the window).

Pitfalls

Don’t benchmark with the HUD on. It adds a small but real render cost. For baseline measurement (e.g. to feed benchmark gate budgets) disable it.

Inline mode changes the available area. Leaving the HUD on while running the demo in inline mode can collide with your logs. Use --ui-height N to reserve enough rows.

Tests

The HUD has targeted snapshot and E2E tests:

cargo test -p ftui-demo-showcase perf_hud cargo test -p ftui-demo-showcase --test perf_hud_e2e

And the E2E gate that exercises the HUD in a PTY:

./scripts/perf_hud_e2e.sh