graphig

Rust → WASM · WebGL2 · renders on the visitor's device

The GPU renderer for large hierarchical graphs.

graphigo draws graphs the size of a codebase — every service, file and function at once — laid out by a deterministic circle-pack that is computed once per change and never oscillates.

core render layout ×3
This layout was computed once. It will not move.

Try it

See it run — on your device.

The live demo renders a real codebase — ripgrep, pre-analysed: ten crates, their files, 2,300 functions, 3,500 call edges — with the exact renderer that ships: deterministic circle-pack, drill-down, bundled edges, the graphigo default theme. A synthetic playground is one click away. It loads ≈14 MB of WASM once; after that, every interaction happens on your device. Nothing phones home, nothing computes on a server.

Runs locally too: unzip, then serve the folder (e.g. python3 -m http.server) — WASM needs http, not file://.

How it's used

Data goes in as a plain hierarchical graph — nodes with a child count, edges with a bundle weight. Children load lazily through a callback you provide; the renderer itself never speaks HTTP.

{ "nodes": [ { "id": "harbor", "kind": "module",
              "label": "harbor", "function_count": 42 } ],
  "edges": [ { "from": "harbor", "to": "atlas",
              "weight": 3, "ordinal": 0 } ] }
// JavaScript — the npm package:
import { mount } from "graphigo";
const g = await mount("#graph", data, { expand, onSelect });
g.replaceRoot(next); g.select(id, kind, label);

// Rust — the crate underneath:
graphigo::start(slice, StartOptions {
    theme, expand, layout: Rc::new(CirclePack), ..
});

Both ship today, source included: the Rust crate and the npm package around it. Custom layouts are plugins — a pure function, validated by a harness before it ever renders.

How it works

Still by construction.

Not a force simulation

Children are packed at fixed positions and the container is sized to exactly enclose them — recursively, once per structural change, never per frame. The layout cannot oscillate, and containment is guaranteed by construction.

Depth you can read

Colour encodes hierarchy depth. Size encodes child count, in four discrete steps — never a continuous scale nobody can read back. Nothing is hidden: every edge is drawn, and parallel edges bundle into one arrow with an ×N label.

Drill without losing your place

Double-click a node to open it into a container and keep going — the camera fits the current level, the breadcrumb carries the path back. From the whole system down to a single leaf.

Layout plugins

A layout is a function.

Structure in, positions out. Layout plugins are pure and deterministic — the renderer owns the transitions and animates them. Want a force layout anyway? Fine: run it to convergence inside your plugin and return the settled positions. The oscillation stays out; your layout gets in.

The plugin spec is a single self-contained document, written so you can hand it to an LLM and generate a working layout. A validation harness checks containment and determinism — a generated layout proves itself before it ever renders.

Theming

Make it yours.

Every colour and every measure — canvas, the depth ladder, ring widths, node size steps, label volumes, edge weights — is a value you own. There are no hardcoded brand colours in the renderer: what you see here is just the default theme. A theme is complete or it is not a theme: take the default, change what you want, pass it back whole. No partial overrides, no silent fallbacks — a missing field is an error that names the field.

import { mount, defaultTheme } from "graphigo";

const theme = await defaultTheme();   // the complete default, yours to edit
theme.accentSelected = "#C2481F";
theme.depthRing = ["#0E7C6B", "#1D5FA8", "#6A46C8", "#A83E9E", "#C2265E"];
await mount("#graph", data, { expand, theme });

See it live: the demo ships a second complete theme — the same real codebase on paper. One object swapped, every colour different, every measure identical. The theme object is right there in the page source.

Rust core, JavaScript API —
graphigo.mount(el, data, options)

Zero runtime network calls.
No telemetry, no phone-home.

Static data or lazy expand —
load children as you drill.

Licensing

Buy the source, not a lock.

Free for non-commercial use

Personal projects, research, evaluation — use graphigo without asking.

Commercial licence, source included

A commercial licence ships with the full source. You can read, build and debug what you ship.

No licence keys, no DRM

Nothing phones home, nothing expires in production. You buy compliance, updates and support — not a lock.

Early access — licensing terms are being finalised and will be published here.