🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman
Session graph viewer for Claude Code and Codex
zoetrope turns Claude Code and Codex transcripts into a live flow graph in your terminal or browser. It shows the main agent, subagents, and tool calls, and it can either replay a saved session or follow a running one as new lines arrive.

Builders who use Claude Code or Codex and want a visual read on what their agent is doing.
You can watch an agent session unfold as a graph, then replay or inspect it without rereading raw logs.
What it does
Live session graph
Draws the main agent, spawned subagents, and tool activity as nodes and edges that update while the session runs.
Replay and follow mode
Replays a finished transcript at session pace, or follows a live session as new events are written.
Inspector panel
Shows the prompt that spawned an agent, its model, reasoning context, and every tool call with timings.
Browser build
Runs the same core in the browser via WebAssembly so you can drop in a transcript without installing anything.
Herdr plugin
Opens the exact session for a Herdr pane and can attach the graph over the agent pane with a hotkey.
How to get it
- 1Homebrew — macOS and Linux
brew install furkankly/tap/zoetrope
- 2Cargo — needs a Rust toolchain
cargo install zoetrope
- 3Whichever route you take, the command is zoe. Or build from source
git clone https://github.com/furkankly/zoetrope cd zoetrope cargo build --release ./target/release/zoe
README
zoetrope
Watch a Claude Code or Codex session as a live flow graph, in your terminal or your browser.
zoetrope.furkankly.dev · the whole app in your browser, the same binary compiled to WASM
Claude Code and Codex write a transcript for every session. zoetrope reads it and draws the session as a graph in your terminal: the main agent, the agents it spawns, and the tools each one runs, updating live as it goes. Point it at a finished run and it replays, paced by the session's own timestamps. Point it at a running one and it follows along. It's read-only, and nothing leaves your machine.
Built on ratatui and rataflow.

Supported agents
| Agent | Sessions live in | Replay | Follow live | Browser |
|---|---|---|---|---|
| Claude Code | ~/.claude/projects/ | ✓ | ✓ | ✓ sessions and subagents |
| Codex CLI and desktop app | ~/.codex/sessions/ | ✓ | ✓ | ✓ sessions and subagents |
zoetrope reads a session from any of its files and tells the formats apart by
content, so zoe <file> works for either, and zoe <id> finds a session by id
across both.

Installation
Homebrew — macOS and Linux:
brew install furkankly/tap/zoetrope
Cargo — needs a Rust toolchain:
cargo install zoetrope
Prebuilt binaries — no toolchain needed. Every
release carries archives for
macOS (Apple Silicon and Intel), Linux (musl, arm64 and x86_64) and Windows
(x86_64). Unpack one and put zoe on your PATH.
Whichever route you take, the command is zoe. Or build from source:
git clone https://github.com/furkankly/zoetrope
cd zoetrope
cargo build --release
./target/release/zoe
No install at all: try it in your browser. Drop a transcript on the page and get the same graph.
In Herdr
Herdr is a terminal multiplexer built for running coding
agents side by side. It knows which agent occupies a pane and the id of the
session running there, so the plugin in
herdr-plugin/
opens that exact session in zoe, without you naming a file or an id.
herdr integration install claude # and/or codex, so Herdr learns session ids
herdr plugin install furkankly/zoetrope/herdr-plugin
herdr plugin action invoke setup-keys --plugin furkankly.zoetrope
Focus an agent pane and press prefix+shift+z. The graph opens over the pane,
follows the session live, and the same key closes it. There are placements for
a split and a tab as well, and the plugin's
README
covers both.

Usage
zoe # follow the current project's live session
zoe <dir> # follow another project's session
zoe <file.jsonl> # replay a recording from the start (any file of a session)
zoe <id> # replay a session by id, or a unique prefix of one
zoe <file.jsonl> --follow # open a recording at its live edge
zoe <file.jsonl> --speed N # playback speed (default 8.0)
zoe --provider codex ... # force the format instead of detecting it from the file
zoe inspect <file|id> # print the session tree and exit (no TUI)
Give it a file and it reads the whole transcript, then keeps watching for new lines. Give it a directory, or no argument at all, and it finds the newest session in that project and follows it live. Whichever way you start, the controls are the same: scrub, follow, pause, jump back to live.
The same engine also runs in the browser, compiled to WebAssembly via ratzilla. Open a session from disk, or drop a transcript on the page. It stays local there too.
Features
The graph
- A node per agent: the main session, its subagents, and workflow groups with their children nested underneath
- Status, current tool, tool count and output tokens on every card
- Edges animate while an agent is working, and settle when it finishes
- Tool calls surface as chips beneath their agent (
⚒ bash ×5, or⚒ bash 0.5sticking during a single call), resolving to✓or✗ - A minimap showing where your viewport sits once the graph outgrows the screen
Time travel
- One scrubbable timeline over both live and replayed sessions
- Indexed by event rather than wall-clock, so a busy minute gets room instead of collapsing into a sliver
- Scrub, pause, step between prompt eras, or snap back to the live edge
- Seek backwards and you see the session exactly as it stood at that moment. Agents un-finish, tool counts fall, the graph shrinks back
- Optional gap compression, to skip dead air or keep faithful real-time pacing
Inspection
- Click any agent for its provenance: the prompt that spawned it, the reasoning around it, its model, and every tool call it made with timings
- Session info overlay: mode, permissions, queued ops, file edits, last prompt
zoe inspectprints the whole tree headlessly, so it runs anywhere without a TTY
Reading your sessions
- Follows a running session live, or replays a finished one
- Reads everything a session writes: the main transcript, its subagents, and workflows with their own children, so the graph is the whole picture
- Keeps going when an agent writes something it hasn't seen: unfamiliar records are skipped, never fatal
- Read-only, and no network at all (see below)
Keys
space play/pause · [ ] prev/next prompt · End or g jump to live · drag the
bar to seek · ? for everything else.
Full keymap
| Key | Action |
|---|---|
space | play / pause (resumes from the playhead) |
[ / ] | previous / next prompt era |
End / g | jump to the live edge |
s | toggle gap compression (faithful pacing vs. skip idle stretches) |
| mouse drag | seek along the scrubber |
o / f | camera: Overview / Follow |
r | relayout (tidy the graph) |
arrows / Tab / Shift-Tab | move between agents |
h j k l | pan the graph |
+ / - / 0 | zoom in / out / reset |
c | center on the selected agent |
| click | open an agent's detail panel |
j / k / PgUp / PgDn | scroll the detail panel |
i | session info overlay |
? | help overlay |
esc | close an overlay / clear the selection |
q / ctrl-c | quit |
j / k scroll the detail panel when an agent is selected, otherwise they pan the graph.
Hand the camera to the action with f and it glides to whichever agent just did
something. This is what watching a live run looks like:

Or drive it yourself: pan, zoom where you point, open an agent's panel, and drag the scrubber to travel back through the session.

Under the Hood
zoetrope treats the transcript as an append-only event log. It tails the files, parses each line defensively, and folds them into a derived model of agents, tool calls, and prompts. Nothing is mutated in place. The model is a pure function of the facts seen so far, so seeking backwards is exact.
A few of the pieces that turn a log into a watchable session:
- Two clocks, kept apart. The playhead runs on content time, the session's own timestamps, and everything the model says is true is a function of where it sits. Presentation time is how long you have been watching, and only animation reads it. Speed, gap compression and scrubbing change how the playhead moves, never what the session says happened.
- One timeline for live and replay. Behind the live edge it paces forward; at the edge it pins and folds in new appends as they land. A live session and a saved recording differ only in where the playhead starts. Same engine, same controls.
- Ground truth over heuristics. Parentage, completion and naming come from what the
format actually records (a subagent's
toolUseId, a workflow'srunId, a journalresult) rather than from guessing at strings or timing. - You own the camera, not the history. The recording is immutable and the graph
never rearranges itself under you (
rto relayout). The camera follows the action until you touch it, then stays where you put it. - Zero network, provably. There is no HTTP client anywhere in the dependency tree,
and
tokiois pulled in without itsnetfeature.cargo treeis the proof. This is a property you can check, not a promise.
The model, the timeline and the rendering all live in the portable core, a
library with no IO that compiles for any target, including WebAssembly. The native
frontend (the zoe binary in this
crate) and the browser frontend (the zoetrope-web crate under web/wasm/, which
runs the hosted app) sit on top of it and differ only in their IO and event loop.
See docs/DESIGN.md for the module map and the transcript format,
and docs/ARCHITECTURE.md for the invariants above in full.
A note on the transcript format
The transcript formats zoetrope reads are undocumented and internal to Claude Code and Codex, so they can change without warning. zoetrope is built to degrade rather than break: unrecognized records are skipped, missing fields fall back, and a malformed line never takes down the session. If a new release of either makes something render oddly, please open an issue.
Contributing
Pull requests are welcome. Support for another agent is one provider directory
under src/provider/; docs/DISCOVERY.md
says what one consists of and what proves it.
- This project follows Conventional Commits for all commit messages (e.g.
feat(timeline): index the playhead by event instead of wall-clock,fix(tailer): fold appends at the live edge without rebuilding). The changelog is generated from them with git-cliff, and non-conforming commits are dropped. - Run
cargo fmt,cargo clippyandcargo testbefore opening a PR. - Those cover the portable core and the native frontend. The browser frontend is a
second crate (
zoetrope-web, inweb/wasm/) that only builds for wasm32, so it is excluded from the root workspace and no rootcargocommand touches it. From the repo root, build it withbash web/scripts/build-wasm.shand lint it withcd web/wasm && cargo clippy(its.cargo/config.tomldefaults the target to wasm32).
License
MIT.
Acknowledgements
Files in the repo
- .github
- assets
- benches
- docs
- herdr-plugin
- src
- web
- .gitignore
- Cargo.lock
- Cargo.toml
- CHANGELOG.md
- CLAUDE.md
- cliff.toml
- committed.toml
- LICENSE
- README.md
- release-plz.toml
- typos.toml
Discussion (0)
Ask about usage, or say what you built with itSign in to join the discussion.
No comments yet. Be the first to say what this is good for.
More tools
The best-benchmarked open-source AI memory system. And it's free.
Orca is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop, mobile and remote runtime.

A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io
Never stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors
Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.