A typed, declarative React library for rendering and exploring graphs with WebGL. Handles 100K+ node graphs. UI components included: search, tables, histograms, and more.
| Pillar | Why it matters |
|---|---|
| Declarative end to end | The graph is a prop — rendering, force layout, transitions, selection, and undo/redo are handled. No imperative canvas code. |
| Built for large graphs | GPU rendering holds 100K+ nodes interactive; incremental filtering keeps the core's per-brush cost near 1 ms at that scale, measured on a disclosed reference machine. |
| Analyst UI included | 14 packaged components — search, minimap, tables, histograms, timelines, legends, inspectors, and more. Headless-styleable, one import each. |
| Testable without WebGL | A headless core and an engine seam with a FakeEngine double; integration tests run in plain jsdom. |
| Typed, honest boundaries | Malformed data degrades with batched diagnostics — never throws mid-render. |
npm install @modernrelay/orbit-react @modernrelay/orbit-core @modernrelay/orbit-engine-cosmosStart with Your first Orbit graph: a complete React + TypeScript app with a three-node graph, search, and controlled selection. The example is compiled and exercised against the published package shape in CI.
The Cosmos engine and OmniGraph adapter require Node.js 22 or newer.
All five packages release in lockstep. See Releases for changelogs.
| Package | Role |
|---|---|
@modernrelay/orbit-core |
Headless core: validation, reconciliation, projection, the instance + store. Subpaths: /engine (the GraphEngine contract), /testing (FakeEngine, worker double). No React or engine imports. |
@modernrelay/orbit-react |
<Graph/>, GraphProvider, 14 packaged UI components, hooks, ref API. React 18+ peer. |
@modernrelay/orbit-engine-cosmos |
The default rendering engine: WebGL drawing and GPU force simulation, built on cosmos.gl. Loaded lazily when the graph mounts. |
@modernrelay/orbit-data |
Prepared-data adapters: rows/CSV/JSON in the root entry; Arrow and Parquet as isolated subpath entries that never reach the root bundle. |
@modernrelay/orbit-omnigraph |
Omnigraph server adapter: streamed export loader, .pg schema tooling, search service. |
Built into <Graph/> itself — no extra imports:
- DOM labels with collision-ranked visibility and a
renderNodeLabelcustom renderer - Lasso selection (freehand polygon)
- Emphasis ring for hover, focus, and keyboard navigation
- Drag & pin handles on nodes
LiveRegion— screen-reader announcements for selection and navigation
Packaged components — each ships as its own entry point
(@modernrelay/orbit-react/components/<Name>), headless-styleable, wired through
GraphProvider context:
| Component | Entry | What it does |
|---|---|---|
GraphExplorer |
components/Explorer |
Unified search, table, passive inspection, bounded expansion, ordered paths, and saved investigations |
GraphSearch |
components/Search |
Search box with debounced queries, result list, keyboard activation |
GraphNavigator |
components/Navigator |
Bounded semantic keyboard navigator (arrow/paging traversal with a11y announcements) |
GraphMinimap |
components/Minimap |
Whole-graph thumbnail with a draggable viewport rectangle |
GraphTooltip |
components/Tooltip |
Hover card for nodes and edges |
GraphInspector |
components/Inspector |
Node, relationship, and selection comparison with passive inspection |
GraphTable |
components/Table |
Virtualized tabular view of nodes or edges, crossfilter-connected text filtering |
GraphHistogram |
components/Histogram |
Crossfilter histogram — drag-brush a numeric dimension to filter the graph |
GraphTimeline |
components/Timeline |
Timeline band over a temporal dimension with brush + playback |
GraphLegend |
components/Legend |
Legend over any scale-valued styling channel, row-click filtering |
GraphToolbar |
components/Toolbar |
Camera & simulation controls: zoom, fit, reset, pause/resume |
GraphContextMenu |
components/ContextMenu |
Right-click / long-press menu on nodes, edges, and background |
GraphSelectionActions |
components/SelectionActions |
Action panel that appears while a selection is non-empty |
GraphSimControls |
components/SimControls |
Force-simulation tunables panel (live, no restart) |
- Data & identity: typed snapshots (objects or columnar), streaming ingest, validation diagnostics, stable identity across updates.
- Layout & simulation: GPU force layout or fixed coordinates, live tunables, pause/resume, zero rAF at rest.
- Interaction: node/edge picking, drag, lasso, pins, context menus, full keyboard navigation with a11y.
- Exploration: scope/isolate, node expansion, groups with collapse, folds, semantic zoom, path emphasis, search.
- Filtering & analytics: incremental crossfilter brushing, hide/dim masks, metrics, scales and legends.
- Appearance: hot-swappable dark/light themes, node image sprites, edge arrows, animated transitions.
- Persistence & export: deep-linkable view state, undo/redo, SVG / streamed JSON / PNG exports.
- Scale: measured performance gates, telemetry snapshots, degradation ladder, off-main-thread data acceptance.
For runnable examples of search recovery, typed pagination, stable layouts, path explanations, and durable checkpoints, see the exploration workflow guide and Exploration → Investigation workflows in Storybook. The demo’s Explore graph button opens the same workspace for generated, CSV, streamed, and Omnigraph data.
All hooks read the instance through GraphProvider (or the nearest <Graph/>):
| Hooks | Surface |
|---|---|
useGraphInstance, useResolvedInstance |
The headless instance itself |
useGraphStatus, useGraphDiagnostics |
Lifecycle + batched diagnostics |
useGraphSelection, useGraphHover, useGraphEdgeHover, useGraphPins |
Interaction state |
useGraphViewport, useGraphSimulationRunning |
Camera + simulation state |
useGraphScope, useGraphOverlays, useGraphPendingExpansions |
Exploration state |
useGraphVisible, useGraphCrossfilter, useGraphTimeline |
Filtering state |
useGraphHistory, useGraphSearch, useGraphTheme |
History, search, theme |
Beyond hooks:
GraphHandle(ref on<Graph/>): camera ops,focusNode,emphasizeNode, fold/expand/group ops,getViewState/setViewState, exports,getPerfSnapshotcreateGraphInstance(core): the same engine-driving instance with no React — everything except the React component layer works headless (data, layout, interaction events, exploration ops, filtering, view state, exports, telemetry)
pnpm install
pnpm check # boundaries + anchor/pin guards + lint + types + tests + gate evaluator
pnpm demo:dev # demo app at http://localhost:5199
pnpm --filter orbit-demo e2e # Playwright suite
node scripts/perf-lite.mjs --tier S # perf measurement (headful, real GPU only)Ground rules enforced by pnpm boundaries and the test suites:
- React never touches per-node hot data; buffers stay below the adapter line
- Core imports no React; nothing outside
orbit-engine-cosmosimports cosmos - One
applyHostUpdate= one store publication = at most one atomic engine commit - Position readback is per-event, never per-tick
- Perf numbers count only from real GPUs, n≥3 runs, variance-qualified — software rasterizers are never accepted
Integration testing: swap the engine for the bundled double —
import { FakeEngine } from '@modernrelay/orbit-core/testing' — and the full
component tree runs without WebGL.
