Why Terminal Multiplexers Are an Anti-Pattern: Lessons from Kitty's Creator
If you're a developer, you probably use tmux or screen. You might even consider it essential infrastructure. But according to Kovid Goyal, creator of the Kitty terminal emulator, terminal multiplexers
If you’re a developer, you probably use tmux or screen. You might even consider it essential infrastructure. But according to Kovid Goyal, creator of the Kitty terminal emulator, terminal multiplexers are architecturally flawed — a design pattern that creates more problems than it solves for local development. His argument isn’t philosophical; it’s deeply technical, and it changed how I think about my terminal workflow.

The Three Fundamental Problems
Goyal identifies three architectural issues with traditional terminal multiplexers that can’t be solved by better implementation — they’re inherent to the design:
1. Doubled CPU Cost
A terminal multiplexer works by implementing a terminal inside a terminal. Every byte of data from your application gets interpreted by two terminal emulators: first the multiplexer, then your actual terminal. This is an unavoidable doubling of processing cost. No amount of optimization in tmux’s codebase can eliminate it — it’s the architecture itself.
For simple text output, this overhead is negligible. For high-throughput scenarios (streaming logs, large file diffs, build output), it’s measurable.
2. Ecosystem Evolution Drag
This is the more insidious problem that most users don’t consider. For any new terminal feature to reach end users, it must be supported by both the terminal emulator and the multiplexer. The multiplexer becomes a gatekeeper for innovation.
Consider Kitty’s graphics protocol, which allows inline image rendering in the terminal. For users running tmux, this feature requires tmux to also implement support for the protocol. Until then, those users simply can’t use it — regardless of what their terminal supports.
This creates a dependency chain where feature adoption is bottlenecked by the slowest-moving component. And when multiplexer maintainers aren’t interested in supporting new features (as happened with Kitty’s attempts to coordinate with the tmux maintainer), the ecosystem stalls.
3. State Machine Bridging Complexity
A terminal emulator is a massive state machine: current text color, decorations, underlines, cursor modes, hundreds of escape code behaviors. A terminal multiplexer must maintain its own state machine AND translate between its state and the host terminal’s state — two slightly different, enormously complex state machines that must be bridged in real-time.
The simplest example: a carriage return (\r) normally moves the cursor to the beginning of the line. In a tmux split pane, the multiplexer must intercept this and rewrite it as a cursor movement code that goes to the beginning of the right pane (which is actually the middle of the screen from the terminal’s perspective). And carriage return is literally the simplest case.
Search any sophisticated terminal program’s bug tracker for “tmux” and you’ll find a category of bugs that only occur inside multiplexers — practical proof of this architectural complexity.
The Alternative: Separation of Concerns
If Goyal were to design a multiplexer, he’d invert the architecture:
- The terminal handles: window management, splitting, visual display (what it’s already designed for)
- The multiplexer handles: session persistence only (the one thing terminals can’t do)
In this model, each window gets a dedicated TTY. No state bridging required. Carriage returns “just work.” Escape codes pass through unmodified. New terminal features work immediately without multiplexer support.
The trade-off: this approach requires the terminal to support specific features, so it’s not universal like tmux. But for users who’ve committed to a terminal emulator, it’s strictly better.
WezTerm has already implemented this approach with its built-in multiplexer. Kitty provides the building blocks through its remote control API.
Kitty’s Building-Block Philosophy
Rather than providing a monolithic multiplexer, Kitty offers composable primitives:
# List all windows with process info (JSON output)
kitten @ ls
# Focus a specific window by match criteria
kitten @ focus-window --match <criteria>
# Focus tabs (OS-level windows)
kitten @ focus-tab <tab-id>
The philosophy: give users building blocks and let them compose their own workflows. This is fundamentally different from tmux’s approach of providing a complete (but rigid) session management system.
For users who use tmux primarily for session switching (not persistence), Kitty’s approach works:
- Query running sessions with
kitten @ ls - Parse with jq to find your target
- Switch to the session tab if it exists, create if not
- Wrap in a script bound to a hotkey
It’s more work upfront, but the result is a workflow that’s exactly what you need — no more, no less.
The One Valid Use Case for tmux
Goyal is direct about where traditional multiplexers remain useful: remote persistence over flaky connections. When you SSH into a server and your connection might drop, you need the running process to survive the disconnection. This requires a server-side process that owns the TTY independent of your connection — exactly what tmux/screen provides.
For local development? Goyal’s position is that multiplexers are “completely an anti-pattern.” Everything you do locally with tmux can be done just as well (or better) with native terminal features.
Remote Control Beyond Local
Kitty’s remote control isn’t limited to the local machine:
- Works within Kitty (local process communication)
- Works from outside Kitty (cross-process on the same machine)
- Can use a TCP port (cross-machine on a local network)
- Fully scriptable and composable
This opens interesting possibilities for custom development workflows that span multiple machines without the overhead of a traditional multiplexer architecture.
What I Changed in My Workflow
After understanding these architectural arguments, I reconsidered my tmux dependency:
- Local development: Switched to terminal-native window management. Splits are handled by the terminal, sessions by tabs/OS windows.
- Remote servers: Kept tmux for the one thing it’s genuinely needed for — session persistence over SSH.
- Custom scripts: Built lightweight shell scripts for the session-switching behavior I actually used tmux for.
The result: fewer mysterious rendering bugs, faster terminal output, and workflows that exactly match my needs instead of conforming to tmux’s opinions about how I should work.
The Broader Lesson
The terminal multiplexer debate illustrates a pattern that shows up across software engineering: solutions that were elegant in one era become legacy drag in the next. Tmux solved real problems when terminals were dumb and couldn’t manage multiple sessions. Modern terminals can do all of that natively — but we keep using the middleman out of habit.
The question isn’t “is tmux bad?” — it’s “does the architecture still make sense given what terminals can do today?” For remote persistence, yes. For everything else, it’s worth asking whether you’re paying an architectural tax for features your terminal already provides.