Hanafuda July card: bush clover with boar

hwatu

A daemon-based web browser for tiling window managers.
Real WebKit rendering, terminal-emulator spawn times.

curl -fsSL https://raw.githubusercontent.com/hongnoul/hwatu/main/scripts/install.sh | bash
13 ms median window spawn
216 ms open→eval→screenshot→close
~56 MB per extra window (PSS)
117k adblock rules, 0 JS

Why

Browsers conflate two things: the engine (slow to start, RAM-hungry) and the window (what you actually ask for). hwatu splits them, the same way emacsclient and wezterm do:

Measured: 13 ms median (p90 35 ms) from hwatu <url> to a mapped, loading window on a warm daemon; background and headless opens land at 14–16 ms. The first-ever window pays a one-time engine/GPU init. Full data: docs/benchmarks.md.

Compared

If you want a minimal browser for a tiling WM (Hyprland, sway, i3, river), the usual suspects trade differently:

hwatu surf qutebrowser luakit
Window spawn 13 ms median (warm daemon) engine start per window engine start engine start
Engine WebKitGTK 6 WebKitGTK 2 QtWebEngine WebKitGTK 2
Tabs none, WM tiles none built-in built-in
Keyboard UI your WM's binds patches vim binds lua config
Memory model one shared engine, N views process per window one big process one process

Philosophy

Usage

hwatu                      # open the launcher (autostarts hwatud)
hwatu example.com          # open a URL (https:// implied)
hwatu list                 # id, url, title of every window
hwatu close 2              # close window 2
hwatu quit                 # stop the daemon

Ctrl+w (or Ctrl+q) closes the focused window. Ctrl+Shift+j / Ctrl+Shift+k scroll the page down/up by half a viewport. The daemon and engine stay warm.

For coding agents

The same daemon doubles as a verification harness for AI coding agents: real WebKit, 14 ms headless windows, a 216 ms open→wait→eval→screenshot→close loop, one JSON protocol over the Unix socket. Full guide: docs/agents.md.

hwatu --headless localhost:3000    # live session, no window at all
hwatu --background localhost:3000  # window mapped, focus stays put
hwatu eval 'document.title'        # run JS in the page, JSON out
hwatu goto /checkout               # navigate + wait for the load
hwatu shot /tmp/page.png           # screenshot (--full = whole document)
hwatu scroll h2 --contains Total   # scroll into view, reports what it hit
hwatu upload 'input[type=file]' ./fixture.png
hwatu focus 3                      # hand the session to the human

Agent CLIs (jcode, Claude Code, Codex, Cursor, ...) are auto-detected and default to --background, so verification never steals your focus. focus promotes any headless or background window to a normal one, session intact.

Tuning

No config file. Engine knobs are set to their correct values in code (GPU compositing always on). The only surface is environment variables:

Variable Meaning
HWATU_HOME Page opened by a bare hwatu. Defaults to the built-in launcher (keybind cheat sheet + URL bar).
HWATU_DISCARD_SECS Seconds an unfocused window keeps its live WebView before being suspended to save RAM (default 120, 0 disables).
HWATU_WEBKIT_FEATURES Ident:on,Other:off — flip individual WebKit runtime features on odd hardware. Unknown identifiers are ignored.

Standard WEBKIT_* and GSK_RENDERER variables pass through untouched. Scrolling smoothness scales with your distro's WebKitGTK: 2.46+ paints with Skia on the GPU.

Architecture

hwatu <url>  --unix socket-->  hwatud (GTK main loop)
                                 ├── prewarmed WebView (adopted instantly,
                                 │   next one warmed in idle time)
                                 ├── window registry (id -> WebView)
                                 └── WebKit: shared network process,
                                     per-site web processes

Unfocused windows are suspended after a timeout: history is serialized, the web process is killed, and RAM comes back. Focus restores them instantly from the prewarm pool.