# claude-office turns your coding agents into a pixel art office

> claude-office is a real-time pixel art office simulation that visualises Claude Code operations: a boss character for the main agent, employee characters for the subagents it spawns, and an office you can watch. The interesting part is not the art but the plumbing, since the event stream that drives it is the same one your hooks write, and the security work in the recent releases is where the design decisions live.

**paulrobello/claude-office** — Real-time pixel art office simulation that visualizes Claude Code operations

- Repository: https://github.com/paulrobello/claude-office
- Stars: 583 · Forks: 127
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/paulrobello-claude-office

## The conceit, and the stack underneath it

Claude Office Visualizer is a real-time pixel art office simulation that visualizes Claude Code operations. Watch as a boss character, the main Claude agent, manages work, spawns employee agents, the subagents, and orchestrates tasks in an animated office environment.

The conceit is the point, but the implementation is the interesting part, because this is a monitoring tool built on top of an agent's event stream rather than a game that happens to have a theme.

The application was built with Next.js, PixiJS, FastAPI and Zustand. That is four choices that each say something. Next.js for the frontend shell, which means the React ecosystem and its server rendering are available if wanted. PixiJS for the rendering, which is the WebGL renderer built for 2D sprites and therefore the right tool for a pixel art scene with many moving characters. FastAPI for the backend, which brings the WebSocket support and the type hints. And Zustand for client state, which is a small store rather than a large state library.

The visual design has a couple of scales. The office view is the main one, the building view shows multiple floors, and there is a floor view and a building settings screen. The Command Center is the other half of the product and it gathers the boss of every live terminal into one cross-session office, so at a glance you can see which terminals need you, which are working, and which are done.

That Command Center feature is the one that turns this from a toy into a tool. One window showing every agent session you have open, with their state, is genuinely useful, and the multi-floor building is the visual metaphor for having several of them.

The project is TypeScript as its primary language, MIT licensed, with the last push on 2026-10-16. The package metadata gives the version as 0.25.0 and the development status as 4 - Beta, requires Python 3.13 or newer, and the classifiers cover macOS, Windows 10, Windows 11 and POSIX Linux, matching the badge that says it runs on Linux, macOS and Windows.

## The changelog reads as a security remediation story

The What is New section is unusually detailed, and it is worth reading as a record of how the project hardened itself over four months.

The v0.23.0 release in July 2026 is an audit remediation with 60 findings, and the list is specific. The API key is no longer disclosed over HTTP, and is delivered via a token launch URL instead. The focus and clipboard features are gated behind the key. Git invocation is hardened against hostile repos. Docker is bound to loopback. There is a real CI workflow running make checkall across all four components. And the backend structural chain was consolidated: a single event-dispatch table, a ConnectionManager moved to the domain layer with functional DI seams, an EventData discriminated union, a BasePoller framework, main.py split into middleware, migrate and websockets, and bounded growth with idle eviction plus replay pagination. That release added 489 tests.

The v0.24.0 release later that month has a breaking change worth flagging. The OpenCode plugin remote backend is now opt-in: a non-localhost CLAUDE_OFFICE_API_URL is clamped to the local default unless CLAUDE_OFFICE_ALLOW_REMOTE=1 is set, matching the Claude Code hooks, and the reason given is that event payloads carry tool I/O and file paths. If you point the plugin at a remote server and do not set that variable, events silently post to localhost.

That is a good design decision and a bad failure mode, and the release note says so plainly.

The same release added a pluggable summary backend, so summaries come from SUMMARY_BACKEND set to claude-cli as the default, any openai-compatible endpoint, or disabled, and the Claude Code OAuth token is no longer required.

And there was a performance rewrite: per-frame agent writes batched into one store update per tick, with the header, sidebar and modals no longer re-rendering at 60fps during movement, the gameStore split into 9 Zustand slices, and single-writer agent-state ownership retiring the 3-second stuck boss watchdog. That entry also records that 67 of 69 audit findings were resolved.

A visualiser for coding agents that ships a security audit, an audit remediation document and hundreds of tests is a different proposition from a screenshot.

## Docker works end to end now, and the details are the useful part

The v0.25.0 release in October 2026 is the one that made deployment painless, and the mechanism it chose is instructive.

Docker deployment works end to end. The compose stack now serves the UI at http://localhost:8050 with no port configuration, because the frontend derives its API and WebSocket origin from the page itself, transcript path translation defaults on, and make docker-config points the hooks and the OpenCode plugin at port 8050.

Deriving the origin from the page is the right call for a tool that people run in different environments, and it removes an entire class of configuration problems.

The compose file itself shows the deployment shape:

```yaml
    ports:
      - "127.0.0.1:8050:8000"
```

The port mapping is bound to loopback on the host, and the comment explains why in terms of the threat model. Loopback-only on the host, because this is a localhost tool, and inside the container requests come from the Docker gateway, so the in-app IP check is disabled via LOCALHOST_ONLY=0 below. That setting is new in this release and defaults to on, with compose disabling it in Docker where the loopback boundary is the host port binding.

So there are two layers of loopback enforcement and each is disabled exactly where it does not apply. That is more careful than most local tools do.

The volumes are the other half:

```yaml
    volumes:
      # Mount Claude data directory (read-only for security)
      # The hooks write JSONL transcripts here; backend reads them for token usage
      - ${CLAUDE_DATA_DIR:-~/.claude}:/claude-data:ro
      # Persist SQLite database
      - claude-office-db:/app/data
```

The Claude data directory is mounted read-only, and the comment explains why: the hooks write JSONL transcripts there and the backend reads them for token usage. The read-only mount is what stops the container from modifying your agent data, and it is also how the tool gets token counts without any Claude API access.

The environment block then handles path translation, since a transcript path written on the host has to be rewritten to the container path. There are two variables for that, the host path prefix that appears in transcript_path from the hooks and the container path where the home directory is mounted, with the second one fixed to /claude-data. The database is a SQLite file on a named volume, so it survives a container rebuild, and the Claude API settings are marked optional with a note that they enable AI summaries.

## Five projects and a hook-driven architecture

The repository layout shows the structure of the system, and it is a multi-project workspace rather than a single application.

There is a backend/ directory, a frontend/ directory, a hooks/ directory, an opencode-plugin/ directory and a scripts/ directory. The frontend is a Next.js project, the backend is Python with FastAPI, and there is a pyproject.toml and a uv.lock at the root alongside a Dockerfile and a docker-compose.yml.

There are also .claude/ and .codex/ directories at the root, which are the agent instruction files for the two coding assistants the project works with, alongside AGENTS.md and CLAUDE.md.

The hooks directory is the mechanism everything else depends on, and the changelog explains what it does. The floor-config hook and the simulation scripts hardcoded the backend address, which is why they broke when the backend ran on another port, and they now read the same environment variables as the rest of the stack. The hooks are what write the JSONL transcripts that the backend reads, and the v0.25.0 release added the ability to recover sessions that were opened before the backend or after a database wipe, which then appear automatically on their next event without resetting a live session's office state. Child OpenCode agent sessions stay excluded.

That recovery feature is a small detail with a big practical consequence. A visualiser that loses its state every time the backend restarts is a toy, and one that recovers sessions is a tool.

The v0.22.0 release added cron helpers that round-trip between a friendly schedule editor and five-field cron expressions, with timesToCron, intervalToCron and cronToEditor. That is the kind of thing a project needs when the simulation involves scheduled events rather than only reacting to what the agent does.

There is also an AUDIT.md, an AUDIT-REMEDIATION.md, an AUDIT_REMEDIATION.md and an ENHANCEMENTS.md at the root, which is a lot of process documentation for a project of this type, and the docs/ directory plus a Makefile with the checkall target the CI runs.

## Where this sits against the ordinary monitoring tools

The comparison is with the ways people usually keep an eye on multiple agent sessions, and the answer is that most people do not.

The usual approach is to have several terminal windows open and glance between them. That works until you have four sessions, at which point you are reading scrollback to work out which one is waiting for input. The Command Center addresses exactly that problem, and it addresses it with a visual metaphor that makes the state legible at a glance, which a table of session names and statuses would also do but less immediately.

Against a proper observability tool, the difference is in what is being observed. A tracing or logging setup tells you what the code did. This shows you what the agent is doing, and it does so from the agent's own event stream, which means it knows about subagents spawning, which is a level of structure a log aggregator would show as a bunch of interleaved messages.

The price is that it is a visualisation of an agent rather than a measurement of a system. You can see that an agent is working and that one needs you. You cannot read the diff it produced or see which files it touched without going to the terminal, although the event payloads carry tool I/O and file paths, so the backend has that information.

The honest caveat is the maturity. This is a Beta project at version 0.25.0 whose changelog for the last four months is largely a security remediation effort. That is a good sign about the author's priorities, and it also means you are adopting something whose interfaces are still moving, with a breaking change in the OpenCode plugin remote backend as recent as July 2026.

For the intended use, which is one developer with several sessions open, it is a distinctive and well-executed answer to a small problem that costs real attention.

## Conclusion

Adopt claude-office if you run several Claude Code sessions and want to see what they are doing at a glance, since the Command Center and the office view give you that and the project runs on Linux, macOS and Windows. Skip it if you need a supported monitoring product, since this is a Beta project at version 0.25.0, and if you want to point it at a remote backend, because the OpenCode plugin now requires an explicit opt-in for that. Verify first how you run it, since the Docker compose stack now works end to end on port 8050 with no port configuration, that your hooks write transcripts where the backend expects them, and whether you want the summary backend on the Claude CLI or an OpenAI-compatible endpoint.

## FAQ

### What does claude-office visualise and what is it built on?

It is a real-time pixel art office simulation that visualises Claude Code operations, showing a boss character for the main agent and employee characters for the subagents it spawns. It was built with Next.js, PixiJS, FastAPI and Zustand, and it runs on Linux, macOS and Windows.

### How do I run claude-office with Docker?

Run `docker compose up -d`. The compose stack serves the UI at http://localhost:8050 with no port configuration, because the frontend derives its API and WebSocket origin from the page itself and transcript path translation defaults on. The Claude data directory is mounted read-only so the backend can read the JSONL transcripts the hooks write, and the SQLite database lives on a named volume so it survives a rebuild.

### Why does the claude-office OpenCode plugin refuse a remote backend by default?

Because event payloads carry tool input and output and file paths, so a non-localhost CLAUDE_OFFICE_API_URL is clamped to the local default unless CLAUDE_OFFICE_ALLOW_REMOTE=1 is set, matching the Claude Code hooks. The release note warns that if you point the plugin at a remote server without that variable, events silently post to localhost.

## Sources

- [Issues](https://github.com/paulrobello/claude-office/issues)
- [License: MIT](https://github.com/paulrobello/claude-office/blob/main/LICENSE)
- [paulrobello/claude-office on GitHub](https://github.com/paulrobello/claude-office)
- [README](https://github.com/paulrobello/claude-office/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/paulrobello-claude-office
