Graph (Network)

The Graph chart renders networks of relationships — entity connections, agent networks, blockchain addresses. Instead of a single force simulation, it is a viewport over a headless graph model with three deterministic views: ego (who surrounds a node), path (how two nodes are connected), and cluster (the shape of the whole network). You never draw the full graph — you draw the answer to a query.

⚠️ Experimental. Graph is not covered by the 1.0 stability guarantee — its layout, rendering, and options may change without a major-version bump. For production reporting, prefer the stable chart types.

Ego view

Nodes snap to a regular grid sized for the neighborhood. The focused node is pinned to the center cell; every category (node group) owns a home corner or edge and fills cells from its far corner toward the center — so the deepest connections sit at the rim and first-ring ones end up next to the ego, and distance from the center still reads as degrees of separation. The layout is deterministic — same data, same picture. Nodes are monochrome with a category glyph; color belongs to the links, and the filled dark node marks the current center. Click any node to recenter on it: the chart queries the source for that node's neighborhood, and nodes present in both views travel to their new positions instead of being redrawn. Zoom out to switch to the cluster overview (semantic zoom).

Path view

"How are A and B connected?" — up to pathCount edge-disjoint routes, laid out left to right: columns are hops, each route gets its own row with a hop-count caption, and the shortest route is visually emphasized while the alternatives recede. Each endpoint's other connections fan out behind it, faded — showing that both nodes are well connected beyond these routes (disable with pathContext: false). Pathfinding is the data source's job (server-side on a real backend), because the client only holds the neighborhoods it has walked.

Cluster view

The whole network collapsed into communities (Louvain). Each meta-node is labelled by its most-connected member; size encodes the community size, link width the number of real edges between groups. Click a community to dive into the ego view of its top member; zoom in to dive back to the last ego view.

Example: warm-intro routing

A CRM-style scenario: the ego view is centered on a sales rep, and target leads — deliberately not connected to the rep — are marked with a star glyph. Clicking a lead answers the question that matters: who can introduce me? The onNodeClick callback switches to the path view with the warm-intro routes; clicking the rep restores the ego view. Category glyphs (person, company, partner org, ★ lead) carry the node semantics, so all the color stays on the relationship types.

Data source

Graph reads data through a source adapter with three async queries — the same contract a real graph backend would implement:

{
  neighbors(id, { depth, types })  // → { nodes, links }         — ego view
  paths(a, b, { k })               // → { paths, nodes, links }  — path view
  aggregates()                     // → { communities, links }   — cluster view
}

For static payloads, RareCharts.memorySource(data) simulates that backend over a full in-memory graph:

const dataSource = RareCharts.memorySource({ nodes, links });
new RareCharts.Graph('#chart', { dataSource, depth: 2 }).focus('some-node');

The option is named dataSource because source keeps its usual RareCharts meaning — the attribution line in the chart footer.

Everything the user walks through accumulates in a client-side model, so revisiting a node doesn't refetch it. Swapping memorySource for a real backend adapter changes nothing else.

Nodes

Each node requires only id. label and every other field are optional; when no label is supplied, Graph displays the id:

Field Type Default Description
id string Unique identifier (required)
label string id Display name
group string default Affects node color; can drive ego sectors via groupBy: 'group'
size number 1 Radius multiplier 0.63; read only with sizeBy: 'field' (nodes are uniform by default)
color string Explicit fill override. By default nodes are monochrome — color encodes link types only
image string Avatar URL, rendered inside the circle
hidden boolean false Keep the node out of the ego view without removing it from the data — for known-but-noisy entities. Runtime equivalent: hide(id) / show(id). The path view still draws routes through hidden nodes

Links

Each link requires source and target node ids:

Field Type Default Description
source string Source node id (required)
target string Target node id (required)
type string 'default' Controls color, dash style, arrow, legend label — and ego sectors
weight number 0.5 01. Observable tie strength from your domain (deal count, transaction volume). Drives line thickness and the choice of a node's primary branch in the ego layout. strength is accepted as a legacy alias
label string Available in tooltipFormat, not rendered on the graph

Note the division of labor: link weight is data you bring (observable ties), while node importance is computed — the model runs degree and betweenness centrality over everything loaded, so "who has the most connections" and "who do the paths run through" are answers, not inputs.

Link types

The type field on a link is resolved against the linkTypes map you pass in. Each entry defines how that type looks:

{
  professional: { color: '#00aaff', dash: null, label: 'Professional' },
  family:       { color: '#00c97a', dash: null, label: 'Family'       },
}
  • color — stroke color for the link line and arrow
  • dash — SVG stroke-dasharray string, or null for solid
  • label — text shown in the legend

If a link type is not found in linkTypes, it falls back to t.muted (theme gray). Five presets are available via RareCharts.linkPresets:

Preset Types
personal professional, family, friendship, investment, philanthropy, education
knowledge partOf, causes, related, example, contradicts, prerequisite, extends
org subsidiary, investment, board, partnership, acquisition, competitor
tech depends, calls, dataFlow, inherits, optional
causal causes, enables, blocks, correlates, weakens

Options

Option Type Default Description
view string 'ego' Initial view: 'ego' | 'path' | 'cluster'
dataSource object Source adapter (see Data source). Optional if you call setData()
depth number 2 Ego neighborhood depth (rings)
groupBy string 'group' Ego sectors by node 'group' or by first-ring link 'type'
sectorLabels boolean false Caption the ego category corners
hiddenNodes array [] Node ids to keep out of the ego view (see the hidden node field)
relationTypes array Initial ego-view relation filter: only the listed types are traversed and drawn — an off-type tie between two visible nodes stays hidden. Links without a type match 'default'. Omit or pass an empty array to show every relation type
interactiveLegend boolean true Legend items filter relation types. A regular click isolates one type; Shift/Ctrl/Cmd-click toggles types in a multi-selection
breadcrumbs boolean true Show clickable semantic history after the user visits more than one view
historyLimit number 12 Maximum number of graph states retained by breadcrumb and back() navigation
onNodeClick string | function 'recenter' 'recenter', a ({ node, event }) => … callback, or null
pathCount number 3 Max routes fetched by connect()
pathContext boolean true Fan the endpoints' other connections behind them in the path view, faded
pathContextCount number 8 Max context ties per endpoint
semanticZoom boolean true Zooming out of ego switches to the cluster view, and back
height number 520 Container height in px
nodeRadius number 22 Base node radius in px. Per-node size multiplies this
nodeIcons object | false built-in set Glyphs inside nodes by group: { group: svgPath } (24×24 viewBox), merged over built-ins for person, company, fund, org, politics, education, crypto, lead, cluster. false renders plain circles
sizeBy string Uniform size by default. 'degree' computes size from connectivity in the accumulated model; 'field' reads the per-node size value
zoom boolean true +/−/reset buttons and drag-to-pan. The mouse wheel is left to page scroll
maxNodes number | 'auto' 'auto' Cap on rendered ego nodes. 'auto' derives it from the canvas size; first-ring and best-connected nodes win. Clicking +N more opens the omitted-node list, where any node can become the new focus
draggable boolean true Nodes can be hand-dragged to fine-tune the picture; positions persist until the next view change
linkTypes object { default: … } Type styling map or a preset
tooltipFormat function built-in ({ node, links }) => html — custom tooltip renderer
duration number 500 Transition ms for view changes; respects reduced-motion

Methods

All methods return the chart instance; fetching and rendering are async — whenReady() resolves when queued view changes are done.

const graph = new RareCharts.Graph('#chart', { dataSource, depth: 2 });

graph.focus('peter-thiel');              // ego view around a node
graph.focus('peter-thiel', {
  types: ['investment', 'professional'], // filter the neighbors() query
});
graph.connect('peter-thiel', 'target');  // routes between two nodes
graph.overview();                        // community overview
graph.setData({ nodes, links });         // static payload: memorySource + focus
graph.add({ links: [{ source: 'a', target: 'b', type: 'deal' }] });
                                         // incremental: merge one news-sized payload
graph.hide('noisy-node');                // declutter; show(id) brings it back
graph.setRelationTypes(['investment']);  // update the current ego filter
graph.clearRelationTypes();              // restore all relation types
graph.back();                            // previous ego/path/cluster state
graph.clearHistory();                    // keep only the current breadcrumb
await graph.whenReady();

add() is built for feed-driven graphs (a news item asserts a new tie): the minimal payload is a single link — endpoint nodes are created automatically, with the label falling back to the id.

Interaction

Recenter on click — clicking a node fetches its neighborhood and re-lays the view around it; shared nodes animate to their new positions so you keep your bearings while walking the graph. In the cluster view, clicking a community recenters on its most-connected member.

Focus + context on hover — hovering a node highlights its direct neighborhood and fades everything else, along with a tooltip listing connections by type.

Filter relations from the legend — click a relation type to isolate it; Shift/Ctrl/Cmd-click toggles types in a multi-selection. Filtering is applied to the neighbors() query and the client-side neighborhood. "Show all" clears the filter. Graphs whose links have no explicit type continue to work as a single default relation type.

Reveal omitted nodes — when the viewport cap removes nodes from the drawing, the underlined +N more note opens an accessible list. Selecting an item recenters the graph on it. Capacity omissions are separate from nodes hidden explicitly with hide().

Breadcrumb navigation — after more than one semantic view has been visited, Graph shows a clickable history of ego, path, and cluster states. Returning through a breadcrumb or back() restores the relation filter together with the view; literal pan and zoom are deliberately reset.

Zoom buttons — +/− buttons zoom, ⟲ resets, dragging the background pans. The mouse wheel deliberately stays with page scrolling.

Node dragging — drag any node to fine-tune the picture (no simulation fights back); hand-placed positions survive re-renders until the next view change.

Hide on right-click — right-click any node in the ego view to hide it (the focused center is exempt); a muted "N hidden · restore" control in the corner brings everything back. Same mechanism as the hidden field and hide(id)/show(id) — the node stays in the model and in path routes.

Semantic zoom — zooming far out of an ego view switches to the cluster overview; zooming into the overview returns to the last ego view. Disable with semanticZoom: false.

Minimal example

new RareCharts.Graph('#chart', {
  linkTypes: RareCharts.linkPresets.personal,
  depth: 2,
}).setData({ nodes, links });

Notes

The demos on this page run on a ~300-node synthetic network around Peter Thiel (generated deterministically — a stand-in for a graph database). The layouts are deterministic by design: rings, sectors, and route maps encode what is known about the data instead of asking a physics simulation to discover it. If a view still looks busy, reduce depth, filter link types, or let the cluster view do the summarizing.