/viz — self-portrait

The skill rendering itself — an interactive guide to what /viz is and how to use it, and a live dashboard of the running server. New here? Start with the README.

⚡ Just want to get going? Skip the story — install and first viz in under a minute.

/viz is a skill that lets an AI coding agent show you something — a chart, a graph, a 3D scene, a state machine, a dashboard, an animated explainer — as a live, hot-reloading web page instead of a wall of text. This page is that skill rendering itself: read it here, then open the Dashboard to watch the real running system.

The problem

You ask your AI assistant to “show me how these services talk,” or “visualize this distribution,” or “draw the state machine.” What comes back?

The terminal can’t render a real graph. Chat artifacts are throwaway and stateless. And there’s no loop — no way for the agent to make a picture, actually see how it came out, and fix it.

The solution

Give the agent a real browser canvas. /viz serves whatever HTML/CSS/JS it writes at a live local URL that hot-reloads on every save, keeps every version in git, and lets the agent screenshot its own output to check the render. One skill turns “describe it in words” into “build it, see it, refine it” — for anything HTML and JS can express, which is basically anything.

Same reason a whiteboard beats a paragraph: some ideas are shaped like pictures.

How it works — the 20-second version

A single tiny Bun web server runs in the background and serves your visualizations from many folders at once: a central scratch library, plus any viz-pages/ folder inside your own repos. Each viz is a folder with an index.html (optionally a live data backend). Save the file → the browser reloads itself. When you’re happy, one command bakes a viz into a single self-contained HTML file you can host anywhere.

You never have to hold any of this in your head — the agent drives it. Want the full picture? The architecture diagrams below break down the runtime and publish lifecycle; the Dashboard shows it running live.

Under the hood

How the machine actually works — the runtime that serves and hot-reloads your vizzes, then the publish lifecycle that ships them. The live Dashboard puts all of this to work against your real library.

Runtime — one server, many roots, hot-reload

User types /viz <slug> Claude runs the skill bootstrap.ts default → CENTRAL mint ~/.viz-pages/<slug>/ commit to central git --local [dir] → HOST <repo>/viz-pages/<slug>/ register .discovered.json print host `git add` hint + flags: --deck · --poster · --global slide deck · self-hero OG card · force central in a repo Bun Server · singleton 127.0.0.1:5180 only 🔒 · serves MANY roots · Bun.serve · idleTimeout 255s containers: … uptime: … Router · longest-known-id-prefix match /_health · /_rescan · /_kit/* · /<id>/* · /<id>/api/* · /<id>/_reload Slug map · id → abs dir sortedIds longest-first rebuilt on boot/scan/_rescan Shadow registry .discovered.json · uncommitted central seeded in code Deep scanner · walks $HOME (boot + /_rescan) finds every viz-pages/ · skips node_modules/.git/Library/dot-folders follows symlinks (loop-guarded) · prune on match · depth cap 12 Static handler injects reload <script> with the viz's id baked in blocks ../ path-escape API loader dynamic import() <id-dir>/api.ts ?t=… cache-bust SSE pump · /<id>/_reload Set<client> · per-id filter · "data: reload\n\n" Debouncer · coalesce 100 ms per id fs.watch — one watcher PER container (recursive, dotfiles skipped) Browser renders <id>/index.html fetch /<id>/api/* GET /_rescan EventSource(/<id>/_reload) → location.reload() ← you are here Central library ~/.viz-pages/ (or ~/.claude/viz-pages/) its OWN git repo · committed by bootstrap … central vizzes · … commits Host repos (N discovered) <any-repo>/viz-pages/<slug>/ EACH its own git repo · committed by host … external vizzes across … repos commit central --local: create + register html fetch / _rescan SSE reload reads · watch reads · watch deep scan finds

Publish lifecycle — from local scratch to the open internet

viz/<slug>/ what you author & commit index.html api.ts (optional) recordings.json (tape) <head> — sole source of truth viz:posture = public · private · local viz:listed = listed · unlisted viz:kind = explanatory · operational build.ts central-only · author action builds to dist · NEVER deploys ✋ secret-scan · HUMAN GATE scan tape for secrets, you sign off — no scrubber inline kit + tape → 1 self-contained .html posture = local ✋ SKIPPED — never leaves machine posture = public hosted as-is · no encryption posture = private StatiCrypt AES-256 seal 🔗 magic link · key in #fragment Static host GitHub / GitLab Pages lobby (index.html) _preamble.html → intro public → full card private → 🔒 link req. unlisted → absent public → OG/Twitter meta → URL unfurls a card dumb host · no server Tape recorder · recordings.json --record → tees live api responses --frozen → serves the tape + banner makes an api-backed viz portable feeds ⊘ stays local

Two lobby details the boxes gloss over (the lobby is the container's front page — the auto-generated index.html card grid; public by default, private behind a _private-lobby marker): a per-container _preamble.html — authored HTML dropped at a container root (next to the slug dirs), injected verbatim above the card grid as an intro, so each publish place sets its own. And rich link previews — every public viz emits Open Graph + Twitter-Card <meta> tags (this is OG unfurl, not oEmbed — a dumb static host has no endpoint to serve oEmbed), so the URL unfurls a card in Slack / Discord / iMessage. The image is a human og.png (1200×630, preferred) or an og.auto.png screenshot from verify --og, degrading to a text-only card when neither is present.

Four sealing/shipping moves the branch above skips: Private lobby — drop a _private-lobby marker in a container root and the whole published site (the lobby page + every public viz in it) sits behind one StatiCrypt password; private vizzes keep their own separate key. Share shim — a sealed page's <head> is encrypted, so it can't carry an OG card; each private (and lobby-sealed public) viz also emits a tiny unsealed shim at a secret StatiCrypt-hash path whose head holds the card and whose body JS-redirects to the magic link — crawlers read the card, humans bounce through. The shim URL is the link you share. rotate revokes + re-mints a viz's magic link, or rotate <container> --lobby the lobby key — old link dies. And deploying is a separate human step the boxes deliberately omit (build.ts NEVER deploys): deploy-all.ts pushes every container that has an executable deploy.sh, skipping any whose viz-pages subtree isn't clean-committed, so what ships always matches git.

Four per-viz axes — declared in <head>, independent, composable

posture — access control

public hosted as-is, anyone with the URL. private StatiCrypt-sealed; the key rides in the magic-link #fragment — possession = access. local never published (the scaffold default). Untagged is a hard error — nothing ships on a guess.

listed — index advertisement

listed gets a card on the lobby. unlisted is still built & reachable by direct URL — just off the lobby. Obscurity, not security: if the name is sensitive, use private + a non-revealing slug.

kind — shelf-life (view-time)

explanatory a timeless diagram; freezing loses nothing (default). operational a live-monitoring tool whose truth expires — gets a louder red frozen banner and a ⚡ index badge. Litmus: “if I froze this, would it still do its job?”

triaged — audit posture, not publishing

triaged means you've consciously reviewed this viz's posture; untriaged is still in the audit backlog. Unlike the other three it never touches publishing — it's a drain-to-zero worklist so a growing library gets swept without a per-viz meeting. Set it from the ⚑ pickers or any row's drawer below.

Also new: standalone .runtime/--local vendors a self-contained server into a host repo so its vizzes run with no skill installed (bun viz-pages/.runtime/server.ts). And the shared kit at /_kit/ (tokens + SVG helpers) is inlined into every published page. Every live viz also injects an anchored review layer — Alt/Option-click any element to drop a comment into comments.json for the agent to resolve. And manage.ts vendor copies a whole viz into another repo as a self-contained, runnable copy (a .vendored.json marker + an auto-installed pre-commit drift guard keep it in lockstep with its origin) — set it up from any row's drawer below.

Install it — however you like

Easiest, and it works across Claude Code, Codex, Copilot, Cursor, Gemini Antigravity, and more:

npx skills add RascalTwo/explorables

That drops /viz into every agent you have installed; update anytime with npx skills update. Prefer no third-party tools? Clone the repo and copy or symlink the skills/viz/ folder into your agent’s skills directory by hand — it’s fully self-contained. The only requirement is Bun.

Invoke it — every way there is

Once installed, just describe what you want to see. The plainest form:

/viz a force-directed graph of this repo's imports

And you never start from scratch twice — just talk to it: “color the bars by team,” “add a legend,” “make it 3D,” “now animate the transition.” Each ask edits the same viz and hot-reloads.

The feedback loop is the point

This is what makes it more than a code generator:

Walkthrough — make your first viz

The fastest way to learn the whole tool is to make one thing. Here’s the arc, and the feature each step quietly teaches:

  1. Ask for it bootstrap

    Say “/viz the states of a checkout flow as a state machine.” The agent scaffolds a viz folder, starts the server if it isn’t running, and hands you a live URL. You installed and configured nothing.

  2. Watch it pick a form visual grammar + kit

    It announces the spatial form first — “rendering this as a state diagram, transitions as labeled arrows” — because a real shape (graph, treemap, Sankey, timeline) beats text in a box. It pulls from the built-in kit so colors and arrows look like one system, not re-guessed hexes.

  3. See it — and see it checked verify

    Before it says “done,” it screenshots the page and reads the console, so “blank page from a typo’d import” gets caught and fixed, not shipped. You just get a working picture.

  4. Refine by talking iterate + hot-reload

    “Mark the retry state red. Add a cancel transition.” Same viz, edited in place, browser reloads on save. Every change is committed to git, so any version rolls back.

  5. Point at what’s wrong review layer

    Alt-click the “paid” node, type “this should loop back to cart on decline.” The pin sticks to that node even as things animate; the agent reads it, fixes it, marks it resolved.

  6. Give it live data backend + tape

    Optional: a per-viz Bun api.ts feeds real data (a shell command, a file) streamed into the page. A tape recorder freezes a snapshot so it still plays away from your machine.

  7. Ship it publish

    One command bakes the viz into a single self-contained HTML file for any static host — public, or encrypted behind a share-link or a one-password whole-site lobby. Its URL even unfurls a preview card.

That’s the spine. Everything else is depth on one of these steps.

…and there’s more than fits here

A sampler of what the agent can reach for — you don’t need to remember any of it:

Slide decks

--deck — arrow-key presentations with reversible build fragments.

Share cards

--poster — a 1200×630 OG image that unfurls as a link preview.

Live backends

api.ts — real data over SSE, with frozen tapes for portability.

Private publishing

StatiCrypt magic-links and one-password lobby seals.

Repo-local vizzes

--local — visualizations versioned alongside a project’s code.

Deterministic management

manage.ts — move, re-posture, and mirror vizzes without hand-editing.

The shared kit

Dark-theme tokens, chrome, and SVG-diagram helpers at /_kit/.

Bulk deploy

deploy-all.ts — ship every set-up container in one git-clean-gated command.

Now go watch the machine run itself → open the Dashboard, which is this very page reporting on its own live server, roots, and history.

Install, then ask for a picture. Under a minute.

1 · Install

Needs Bun (bun --version should print a number). Then, in a terminal:

npx skills add RascalTwo/explorables

Works across Claude Code, Codex, Copilot, Cursor, Gemini Antigravity, and more — it installs /viz into each agent you have. Update later with npx skills update. (No npx? Clone the repo and symlink skills/viz/ into your agent’s skills dir.)

2 · Invoke

In your agent, just describe what you want to see:

/viz a treemap of this folder's file sizes

It picks a form, builds it, and hands you a live, hot-reloading URL.

3 · Loop

That’s the whole rhythm. Extras when you want them: --deck (slides), --poster (share card), --local (a viz inside a repo), a live api.ts backend, and one-command publishing.

Want the why, the how, and a guided tour? → read the full README. Or just poke the Dashboard.

The live operational view — the running server's status, every viz it's serving right now, and recent history, read straight from your machine. Live only: on a static copy this shows nothing — run the skill locally for data. New to all this? The README explains the whole system.

Active vizzes

loading…

Server

loading…

Recent commits (central)

loading…