Zero-latency typing
Test methodology ⋅ Results ⋅ Large files ⋅ Why latency matters ⋅ Takeaway
Typing latency is the time between pressing a key and seeing the result on screen.
It is one of the most direct measures of how responsive an editor feels, but relatively few editors publish measurements for it. Startup time, indexing, and file-opening speed matter too, but once the editor is open, typing latency is what you interact with continuously.
Test methodology
Measurements were taken using Keypress.sh, which measures the time from a simulated keypress until the updated frame is presented on screen.
| Component | Value |
|---|---|
| Hardware | Mac mini M2 Pro, 16 GB RAM |
| Operating system | macOS 15.7.5 |
| Display | 60 Hz |
| Test files | sqlite3.c (151,639 lines) gcc.c (753,821 lines) |
| Sample size | 200 measured keypresses per benchmark |
| Configuration | Default settings with syntax highlighting and line numbers enabled |
Graphical applications were launched directly from their bundled Mach-O executables. Inter-key delays were randomized to avoid giving editors a predictable synthetic typing cadence.
On a 60 Hz display, a new frame can be presented approximately every 16.7 ms. Results therefore tend to cluster around display intervals such as 16.7 ms, 33.3 ms, and 50 ms.
Why P95?
The median describes a typical keypress. P95 describes the latency within which 95% of measured keypresses were displayed.
This makes P95 useful for finding the occasional slower frames that can make an otherwise fast editor feel inconsistent.
Tail spread is P95 minus the median. A smaller spread means latency remains more consistent across the test.
Results
sqlite3.c
The first test uses sqlite3.c, a 151,639-line C source file.
| Editor | Median | P95 | P99 | Max | Tail Spread |
|---|---|---|---|---|---|
| TextMate | 14.4 | 16.1 | 17.4 | 17.7 | 1.7 |
| Koi | 14.2 | 16.3 | 17.0 | 17.1 | 2.0 |
| Xcode | 14.7 | 17.2 | 25.1 | 28.2 | 2.6 |
| Vim | 14.5 | 17.5 | 46.7 | 47.6 | 3.0 |
| Lite XL | 13.8 | 25.4 | 65.2 | 74.8 | 11.6 |
| Sublime Text | 18.1 | 30.0 | 31.5 | 32.1 | 11.9 |
| VS Code | 17.1 | 35.3 | 43.9 | 46.0 | 18.2 |
| Zed | 28.6 | 38.2 | 42.1 | 42.4 | 9.6 |
| Neovim | 48.7 | 52.3 | 53.9 | 54.0 | 3.6 |
| BBEdit | 42.4 | 74.6 | 104.5 | 114.5 | 32.2 |
| Emacs | 55.1 | 85.2 | 87.0 | 87.1 | 30.1 |
Koi measured 16.3 ms at P95, close to one refresh interval on the 60 Hz test display. With the same file displayed in two panes:
| Editor | Median | P95 | P99 | Max | Tail Spread |
|---|---|---|---|---|---|
| Koi | 14.0 | 16.0 | 17.4 | 17.4 | 1.9 |
| Lite XL | 24.4 | 27.0 | 35.0 | 35.2 | 2.6 |
| Xcode | 17.2 | 27.7 | 29.4 | 29.7 | 10.6 |
| Sublime Text | 22.9 | 29.8 | 32.8 | 33.5 | 6.9 |
| Zed | 36.7 | 44.1 | 49.5 | 50.3 | 7.4 |
| VS Code | 26.4 | 45.6 | 51.2 | 52.4 | 19.2 |
| Vim | 43.6 | 48.0 | 48.7 | 48.7 | 4.4 |
| Neovim | 49.4 | 52.4 | 53.4 | 53.5 | 3.0 |
| BBEdit | 36.1 | 78.7 | 110.2 | 111.2 | 42.6 |
| Emacs | 91.4 | 106.2 | 108.6 | 109.5 | 14.8 |
Koi measured 16.0 ms P95 with the document visible in both panes.
gcc.c
The second test uses gcc.c, a larger 753,821-line C source file.
| Editor | Median | P95 | P99 | Max | Tail Spread |
|---|---|---|---|---|---|
| Koi | 14.5 | 16.6 | 17.3 | 17.7 | 2.2 |
| Xcode | 25.2 | 27.6 | 29.5 | 30.8 | 2.3 |
| Sublime Text | 20.2 | 30.4 | 31.6 | 31.6 | 10.2 |
| TextMate | 40.7 | 42.6 | 43.3 | 43.5 | 1.8 |
| Vim | 43.2 | 47.8 | 48.8 | 48.9 | 4.6 |
| Neovim | 49.7 | 52.9 | 56.5 | 57.8 | 3.2 |
| Zed | 43.2 | 57.3 | 64.8 | 65.0 | 14.1 |
| Emacs | 87.0 | 118.3 | 121.3 | 122.0 | 31.3 |
| Lite XL | 18.9 | 119.8 | 171.2 | 201.9 | 100.9 |
| VS Code | 27.9 | 171.9 | 178.9 | 181.4 | 144.0 |
| BBEdit | 226.8 | 301.6 | 307.1 | 517.8 | 74.8 |
Koi measured 16.6 ms at P95. For comparison, Sublime Text measured 30.4 ms, Zed 57.3 ms, VS Code 171.9 ms, and BBEdit 301.6 ms. With the same document displayed in two panes:
| Editor | Median | P95 | P99 | Max | Tail Spread |
|---|---|---|---|---|---|
| Koi | 14.6 | 16.4 | 17.5 | 17.7 | 1.8 |
| Lite XL | 13.4 | 32.0 | 110.3 | 152.2 | 18.6 |
| Sublime Text | 23.7 | 32.2 | 35.7 | 36.1 | 8.5 |
| Xcode | 25.1 | 34.8 | 36.1 | 36.8 | 9.7 |
| Vim | 44.4 | 48.6 | 51.2 | 51.5 | 4.2 |
| Neovim | 50.0 | 53.6 | 57.1 | 57.2 | 3.6 |
| Zed | 52.7 | 70.0 | 73.9 | 75.0 | 17.3 |
| VS Code | 124.3 | 172.4 | 176.1 | 178.3 | 48.1 |
| Emacs | 147.3 | 177.3 | 179.7 | 180.7 | 30.0 |
| BBEdit | 251.6 | 304.1 | 310.6 | 524.4 | 52.4 |
Koi measured 16.4 ms P95 with the document displayed in two panes.
Large files
The same measurement becomes more interesting as file size increases. The table below shows P95 typing latency for each file size that passed the large-file usability benchmark:
| Editor | 1M lines | 5M lines | 10M lines | 20M lines |
|---|---|---|---|---|
| Koi | 19.3 ms | 20.5 ms | 31.0 ms | 37.3 ms |
| Vim | 18.2 ms | 18.7 ms | 73.0 ms | - |
| Sublime Text | 31.6 ms | 31.8 ms | - | - |
| VS Code | 50.6 ms | 207.0 ms | - | - |
| Lite XL | 98.2 ms | 129.9 ms | - | - |
| Zed | 164.9 ms | - | - | - |
| BBEdit | 812.1 ms | - | - | - |
Koi measured 20.5 ms P95 at 5 million lines and 37.3 ms at 20 million lines. See Benchmarks for memory usage, usability criteria, editor configurations, and the complete large-file results.
Why latency matters
Typing is one of the few operations an editor performs continuously while you work.
Every keypress can trigger highlighting, parsing, diagnostics, completion, rendering, plugins, and other editor work. As files become larger and more features become active, that work can begin to show up as delayed or inconsistent frames.
Low median latency is useful, but consistency matters too. An editor that usually responds quickly but periodically misses several frames can still feel noticeably less responsive.
Koi is designed to keep the path from keypress to rendered text short and predictable, even as document size increases.
Takeaway
On the 60 Hz test system, Koi measured approximately one display interval at P95 in both source-file tests:
- sqlite3.c: 16.3 ms
- sqlite3.c, dual pane: 16.0 ms
- gcc.c: 16.6 ms
- gcc.c, dual pane: 16.4 ms
In the separate large-file benchmark, P95 remained at 37.3 ms with a 20-million-line file.
The goal is not simply to produce a low number in a small-file benchmark. It is to keep typing latency low and consistent as the amount of work around the editor increases.
Related: