All features

When it isn't the event loop

Try the real interface

Explore this workspace.

Real Torbie UI · mock sessions
Tab bar
Loading Torbie…
Technical background

Nothing blocks, the loop stays free, and the screen still lags — because frames are being dropped, or because xterm is taking a long time to lay out what was written to it. Both are timed and tallied into the same log, and a healthy hour writes no lines at all.

The problem

The stall recorder covers the event loop. It says nothing about the other way a terminal feels slow: nothing blocks, the loop stays free, and the screen still lags — because frames are being dropped, or because xterm is taking a long time to parse and lay out what was written to it.

How it works

tabby-render-timing is a builtin that times both and writes records into the same log. It is a TerminalDecorator, not an edit to the frontend — add-only, so it costs nothing at the next rebase, and it reaches any frontend exposing an xterm without knowing which. It wraps xterm.write and measures call → callback, which is the interval that matters: the caller's await returns long before the screen reflects anything.

A render-timing record
render-timing  frames: 327, slowFrames 3, jankFrames 2, worst 150ms, p50 8.3, p95 8.5
               term1: 7 writes, mean 22.7ms, worst 81ms, 2 slow
Details
  • Tallied, never streamed. A busy terminal writes thousands of times a second; the finding is a distribution.
  • The rAF loop only runs while something is writing, and stops two seconds after. A permanent one would keep the compositor awake on an idle window — a poor trade for a diagnostic.
  • Gaps over 500 ms are not counted as dropped frames. An idle tab produces one enormous gap, and counting it would make every summary look catastrophic.
  • Reports only when there is something to say: a healthy hour writes no lines.
What this does not claim
  • The first version reported nothing at all, because it used the diagnostics note() API — which only appends a breadcrumb shown as context when a stall is reported, and is invisible otherwise. report() was added alongside it for records that are the finding rather than context for one.
Source commits
DiagnosticsAdded 2026-09-0410 files+427 / −4 lines