JSON format
The visualizer reads a single JSON document with a top-level
nodes array. Each node carries its outgoing edges as a list
of target node ids. The whole file is read in the browser — nothing
is uploaded.
Schema
{ "nodes": [
{ "id": string | number, "label": string, "type": string, "edges": Edge[], "meta": any }, ...
]
}
type Edge =
| string | number | { "nodeId": string | number,
"edgeType": string }
Fields
id— required- Unique identifier for the node. May be a string or number; numbers are coerced to strings internally. Duplicate ids cause a parse error.
label— optional- Display text shown in the info panel when the node is selected. Defaults to
id. type— optional-
Free-form kind classifier — for example
"package","file","class". Distinct values are interned the same way edge types are, and the selected-node panel surfaces the type alongside id and degree counts. edges— optional-
An array of outgoing edges. Each entry is either a bare target id
(string or number) or an object
{ nodeId, edgeType }wherenodeIdis the target andedgeTypeis a string label classifying the relationship ("calls","depends-on", etc.). Both forms can be mixed in the same array. Edges referencing unknown ids are dropped with a console warning; duplicate(from, to)pairs are collapsed (first edge type wins); self-loops are dropped. meta— optional- Any user-defined object. Rendered as a key/value list in the selected node panel. Top-level keys with string/number/boolean values render as text; nested objects are serialized as JSON.
Examples
Minimal
{
"nodes": [
{ "id": "a", "edges": ["b", "c"] },
{ "id": "b", "edges": ["c"] },
{ "id": "c" }
]
}
With labels
{
"nodes": [
{ "id": "auth", "label": "Authentication service" },
{ "id": "billing", "label": "Billing service", "edges": ["auth"] },
{ "id": "reports", "label": "Reports", "edges": ["auth", "billing"] }
]
}
With meta
{
"nodes": [
{
"id": "src/index.ts",
"label": "index.ts",
"edges": ["src/util.ts", "src/api.ts"],
"meta": { "lines": 142, "owner": "platform" }
},
{
"id": "src/util.ts",
"label": "util.ts",
"meta": { "lines": 38 }
},
{
"id": "src/api.ts",
"label": "api.ts",
"edges": ["src/util.ts"],
"meta": { "lines": 207 }
}
]
}
With typed edges
{
"nodes": [
{
"id": "AuthService",
"edges": [
{ "nodeId": "User", "edgeType": "depends-on" },
{ "nodeId": "TokenStore", "edgeType": "depends-on" },
{ "nodeId": "Logger", "edgeType": "calls" }
]
},
{ "id": "User" },
{ "id": "TokenStore", "edges": ["Logger"] },
{ "id": "Logger" }
]
}
With node types
{
"nodes": [
{
"id": "@acme/auth",
"type": "package",
"edges": ["@acme/auth/auth-service.ts", "@acme/auth/session.ts"]
},
{
"id": "@acme/auth/auth-service.ts",
"type": "file",
"label": "@acme/auth/auth-service.ts",
"edges": [
{ "nodeId": "@acme/auth/session.ts", "edgeType": "imports" },
{ "nodeId": "@acme/auth/types.ts", "edgeType": "type-imports" }
]
},
{ "id": "@acme/auth/session.ts", "type": "file" },
{ "id": "@acme/auth/types.ts", "type": "file" }
]
}
Notes
- The graph is treated as directed —
edgeson node A pointing at node B means "A → B". - Communities are computed automatically with Louvain modularity clustering.
- Initial positions are computed with a d3-force simulation. Layout runs in a Web Worker and emits ~12 batches before settling.
- The renderer is WebGL2-instanced; 50k+ nodes are practical on modern hardware.