hwatu is a warm WebKitGTK daemon plus a thin client, split the way emacs/emacsclient are. The daemon owns the engine, a compiled adblock ruleset, and a prewarmed WebView that has already loaded about:blank, so its web process and GPU compositor path are hot before any request arrives. Opening a window is: take the warm view, put it in a GtkWindow, start the navigation, warm the next view during idle time. Median 13 ms for a focused window, 14 ms headless, measured over 20 runs (scripts/bench-spawn.sh).
The part I actually care about is verification, not automation. The agent-facing primitives are things like:
- `hwatu check <url> --eval <js> --shot`: open headless, wait for load, eval, screenshot, close, one command, one JSON reply, ~35-39 ms median warm. Same pass through a kept-warm Playwright server with a fresh client per check (the shape every shell-driven agent has) is 341 ms, mostly Node startup and WebSocket connect.
- `hwatu diff --id 2 --other 1`: pixel match percent + diff regions + heatmap between two live windows. A score an agent can climb, not a golden-file pass/fail. We ran an agent against a clone of stripe.com and watched it go 85.1% -> 98.8% match.
- `hwatu motion`: animations as numbers (duration, easing, velocity), and `seek`/`clock` to pin all animations at time t so two screenshots are byte-identical.
- `hwatu focus <id>`: headless is a window property, not a launch mode. When the agent hits a CAPTCHA or a judgment call, focus materializes the same live session (cookies, scroll, half-filled form) in your tiling WM. You act, it takes back over.
Honest caveats, since HN will find them anyway: Linux-only (Wayland best), renders WebKit not Chromium so you still want a Playwright matrix in CI for engine bugs, Playwright wins cold start (190 vs 435 ms, paid once per boot) and memory, and the click/type events are synthetic JS events today (isTrusted false, cross-origin iframes unreachable); trusted GTK-level input is on the roadmap.
It speaks MCP (`hwatu mcp`, stdio, no SDK, translates tool calls onto the same Unix-socket protocol), plain CLI, or one newline-delimited JSON line over the socket. One static binary (~624 KB client, 1.1 MB daemon) plus your distro's webkitgtk-6.0; no Node, no 170 MB browser download.
All benchmark methodology and every caveat: https://github.com/hongnoul/hwatu/blob/main/docs/benchmarks....
Happy to answer anything about the WebKitGTK internals, the prewarm pool, or where the milliseconds go.