DEV Community

Cover image for Building Prism: a 100%-offline 3D visualizer for your AI coding history
Harish Kotra (he/him)
Harish Kotra (he/him)

Posted on AI-assisted

Building Prism: a 100%-offline 3D visualizer for your AI coding history

How a stdlib-only Python server, a vendored three.js snapshot, and a strict "metadata-only" contract turned scattered agent session stores into an explorable city — with no network, no database, and no build step.

The problem: my agents work while I sleep, and I have no idea what they did

I run half a dozen AI coding harnesses — Claude Code, Codex, OpenCode,
Antigravity, Pi, Goose… Each one keeps its own session store, in its own
format, in its own corner of my home directory: JSONL files here, SQLite
databases there, proprietary blobs somewhere else. I had hundreds of sessions and zero answers to simple questions: which project eats my tokens? When do I actually work? What does each model cost me? What tools get used?

The existing "solutions" were dashboards that wanted my API keys, my prompts, my code — uploaded to somebody's cloud. That felt backwards. The data was already on my disk. What I needed was a reader, not another service.

So I built Prism: open a localhost page, fly through your coding life in 3D.

Every design decision follows from three constraints: offline (zero runtime
network), read-only (never touch the harness stores), metadata-only
(never read a prompt, reply, or file).

Architecture: boring on purpose

Architecture: boring on purpose

The whole backend is Python stdlib onlyhttp.server, sqlite3,
argparse, unittest. No pip install step exists, which means the install script is basically "make a venv, download one JS file, run":

./install.sh          # venv + vendor three.js (SHA256-pinned) + serve
python3 server.py --export prism-data.json --no-browser   # headless dump
Enter fullscreen mode Exit fullscreen mode

The one network call in the project's life happens at install time
(curl three.js r128, hash-verified), and CI enforces the invariant forever:

make offline-check    # grep web/ for remote URLs → fail the build if any
Enter fullscreen mode Exit fullscreen mode

Frontend is vanilla JS with no bundler. Each lens is a single file exporting { create }, and create(payload) returns { group, desc, legend, camera, tick, raycast, dispose }. That tiny interface is why nine lenses were cheap to build: Cityscape, Galaxy, Constellation, Timeline, Energy, Calendar, Tools, Hours, Models.

The heart: a schema that says "no"

The most important file is core/schema.py. Every reader normalizes into one document, and the schema gate rejects it before serving if anything is off — NaN/Infinity, negatives, boolean-as-number, malformed nested lists. A drifting upstream store format degrades to status: unavailable in the UI, never a 500.

A session (trimmed from a real export):

{
  "harness": "claude_code", "project": "flybench",
  "model": "claude-sonnet-4-6", "startedAt": "2026-07-09T14:02:11+00:00",
  "durationSec": 412,
  "tokens": { "fresh": 12011, "output": 3402, "total": 15413 },
  "tools": [{ "name": "Bash", "calls": 41, "errors": 2 }],
  "costUsd": 0.31, "costKnown": true
}
Enter fullscreen mode Exit fullscreen mode

Notice what's absent: no prompts, no replies, no code. That absence is load-bearing — it's what makes "read my agent history" feel safe.

Readers: the extension surface

Each harness is one ~100-line file. The OpenCode reader shows the whole
pattern — including the discipline that matters most:

con = C.sqlite_ro(db)   # file:path?mode=ro + 500ms busy_timeout, ALWAYS
try:
    cur.execute("SELECT id, name, worktree FROM project")  # metadata tables
    # …never the message/part tables. Those hold conversation content.
finally:
    con.close()
Enter fullscreen mode Exit fullscreen mode

Three helpers do the heavy lifting across all 28 readers: iter_jsonl
(skips malformed lines), sqlite_ro (read-only open), and
finalize_reader_payload (newest-first sort, 500-session cap, totals, meta).

Adding a harness is a 30-line file plus one fixture test — several Tier-2
readers (Cursor, Zed, Ollama…) sit stubbed in readers/ waiting for someone with the app installed to map the real layout.

Rendering 500+ sessions at 60fps with 2017-era three.js

The trick is old and unglamorous: InstancedMesh. Cityscape draws every session building in a single draw call; per-frame updates only rewrite instance matrices and colors for the time-scrub reveal. Same for the timeline river (markers + spikes), the calendar wall, the treemap. r128 is pinned because it's the last version that loads as one global script with zero modules — no bundler, no import maps, works from file:// if needed.

The fun engineering was anti-blob work. Real histories clump: same-day
sessions stack exactly, daily-driver projects form complete graphs, one tool (exec_command, 12k calls) eats the scene. Each lens got a declutter rule: timeline fans collisions into per-harness lanes, constellation keeps only ≥2-shared-day edges capped at top-2 per star, tools are log-scaled, treemap areas stay linear so "what you see is what you pay."

My favorite lens technically is Cityscape's true-shape maps: each of the 10 cities is hand-charted polygon data — Manhattan's diagonal island, the Seine S-curve with Île de la Cité, Sydney's branching harbour — with districts rejection-sampled onto real land, clustered downtown. No downloaded tiles, no textures; water is a plane, streets are line segments, landmarks are primitives. A subway map, not a satellite photo — and instantly readable.

Lessons worth stealing

  1. Constraints are features. "No network, no writes, no content" killed entire categories of bugs (auth, sync, PII leaks) before they were born — and it's the whole pitch.
  2. A validation gate beats defensive rendering. One strict schema check means nine lenses never think about malformed data.
  3. Stdlib-only is a superpower for tools like this. The install is 30 seconds because there's nothing to install.
  4. Graceful degradation > coverage. 8 verified readers + 20 honest "unavailable" stubs beats 28 half-broken parsers.

Prism is MIT-licensed. If you run coding agents, your history is already on your disk — go look at it: ./install.sh.

Code & more: https://www.dailybuild.xyz/project/249-prism

How this looks

How this looks 1

How this looks 2

How this looks 3

How this looks 4

How this looks 5

How this looks 6

Top comments (0)