@mizansec/ogjs
v0.5.4
Published
Ogjs — high-performance, large-scale graph visualization. GPU force layout in shaders, WebGL2 instanced rendering, grouping/filtering/timeline/geo/annotations, undo-redo, React/Vue/Svelte wrappers.
Maintainers
Readme
Ogjs
High-performance, large-scale interactive graph visualization for the web.
Capability status and open work remain in a private roadmap until publication.
Ogjs is built for large interactive graphs in the browser around three ideas:
- Structure-of-Arrays core — every node/edge attribute lives in a flat typed array. No per-element objects, no GC pressure, cache-friendly hot paths for layout, picking, and GPU upload.
- WebGL2 instanced rendering with a shared position texture — the graph
body draws through bounded instanced passes. Node positions are
uploaded once per frame into a single RGBA32F texture that both the node and
edge shaders sample via
texelFetch, so per-frame upload cost is onetexSubImage2Dregardless of edge count. - Worker-based Barnes-Hut layout — the force simulation runs in a Web Worker over transferable buffers and streams position frames back. The main thread never blocks; the graph animates live while it settles.
Reproducible performance measurement
Run the performance lab on the
target device. Its four tasks measure clustered, degree-hub, hub-and-spoke, or uniform workloads,
visibly arrange with the CPU worker or GPU path, filter/highlight, and exercise up
to 150 visual groups with explicit CPU timing and live node/edge counts. The motion
check reports observed frame intervals rather than derived FPS. The measurement
task supports up to 1M nodes and 2M
edges, combines CPU submission with GPU timer-query duration for each measured
frame, and downloads a versioned
JSON report containing build time, median/p95 frame time, picking throughput,
worker/GPU layout time, allocated typed-array bytes, viewport, and device
capabilities. npm run evidence:performance compares a fresh run with the
checked-in macOS/headless-Chrome/SwiftShader profile and rejects incompatible
environments or regressions. It is a named regression profile, not a universal
hardware speed claim.
For competitive evidence, the dedicated certification harness captures five
independent Ogjs and licensed-reference runs at 10k, 50k, 100k, 250k, 500k,
and 1M nodes in matched browser sessions with a recorded cooldown. It refuses
a superior verdict for incomplete ladders, software
renderers, mismatched machines/browsers/viewports, missing metrics, or gains
below the chosen threshold. See
performance certification.
Quick start
The npm package ships JavaScript and TypeScript declarations only. Applications self-host fonts, icons, images, maps, and other content assets.
import { Ogjs } from 'ogjs';
const og = new Ogjs({ container: '#viz' });
og.addNodes([
{ id: 'a', label: 'Alice', size: 8, color: '#4f8cff' },
{ id: 'b', label: 'Bob' },
]);
og.addEdges([{ source: 'a', target: 'b' }]);
// rule-based styling, evaluated once, written into typed arrays
og.styles.addNodeRule(({ degree }) => ({ size: 4 + Math.sqrt(degree) }));
// OGJS chooses a suitable existing layout, animates it, and frames the result
await og.arrange();
og.on('nodeclick', (e) => console.log('clicked', og.graph.getNodeId(e.nodeIndex)));
const hit = og.hitTest(320, 180); // canvas-local screen coordinates
// one coordinated node/edge tween; invalid ids or edge indices are ignored
await og.animate({
edgeIndices: [0],
edgeWidth: [4],
edgeColor: ['#ef4444ff'],
}, 300, 'quadraticInOut');Advanced capabilities
The advanced-capabilities guide covers the shared primitives behind Ogjs's editing, analysis, temporal, geographic, and evidence workflows. Each capability has a runnable browser example.
- Stable editing: stable relationship IDs, rollback-safe synchronous transactions, and grouped undo (run).
- Editing and edge presentation: validated gestures, deterministic routes, captions, badges, outlines, halos, and pulse (run).
- Worker insights: cancellable weighted or unweighted centrality, spanning-forest, shell-layer, and multilevel or Leiden community jobs over stable snapshots (run).
- Temporal playback: node and relationship intervals, atomic multi-window queries, exact seek, trailing windows, grouped lanes, adaptive detail/aggregate views, range selection, and playback (query controls; playback).
- Geographic investigation: public TopoJSON atlas decoding, self-hosted raster, GeoJSON/MVT feature sources, heat, graph clustering, and spatial queries through one camera; wrapping references keep lines and polygons crossing the antimeridian from spanning the world, reject rings that wind around it more than once, and keep active static vectors editable in SVG (run).
- Measured evidence: portable correctness plus compatible-profile visual and performance comparisons (run).
- Projection pipeline: named transactional node and edge fields reused by filters and styles (run).
- Helm controls: fuzzy node search, semantic legends, and operation progress in one accessible, mountable surface (run).
- Hierarchy workbench: promote a node or an edge, change flow and spacing, rank sibling lanes without splitting branches, and pack a generated five-level hierarchy and companion components without overlap (run).
- Progressive exploration: reveal retained branches, fold at stable zoom bands, and compose atomic structural stages (drilldown; zoom folding).
- Annotation authoring: draw, select, align, resize, style, duplicate, and export persistent graph markup from an optional entry point (run).
API overview
| Area | Methods |
|---|---|
| Data | stable node/edge IDs; addNodes/addEdges, updateNode/updateEdge, removeNodes/removeEdge, atomic edit(label, apply), setGraph, clear, expand |
| Layouts | layouts.forceGPU() (GPU), layouts.force() (worker), size-aware hierarchical, radial, scoped circular / track / temporal / orbit / settleSpacing, retained temporal guides, grid, random, animatedLayouts.*, transitionLayout(apply, { fit }), stop() |
| Animation | animate(...) for one attribute tween; animateKeyframes(frames, { signal?, onStep? }) for sequential attribute/layout/camera frames with optional delayed camera motion |
| Styling | styles.addNodeRule/addEdgeRule — node marks/images, weighted dial parts with optional donut inset, beacon signal rate, plus per-edge width, color, curvature, dash, arrow, label, badge, outline, halo, pulse, and hidden state; deeply frozen prismPack light/dark tokens and createPrismLegend() |
| Editing | forge.join/retarget/scale/rename/grid/acceptDrops/onChange, shared validation, Escape cancellation, atomic undo |
| Transformations | atomic ordered fold/spawn/absorb stages, zoom bands, groupBy/ungroup, composable addFilter/addEdgeFilter/clearFilters, contained groupVisually/growVisualGroup/nestVisualGroups, and retained activation via trailGuide and og.unfold |
| Projections | projections.defineNode/defineEdge, nodeValue/edgeValue, explicit refresh() after data changes |
| Algorithms | paths/traversal/components plus cancellable worker insights.run() for weighted or unweighted strength degree, positive-distance closeness and betweenness, spanning forests, shell layers, and deterministic multilevel or opt-in Leiden communities |
| Timeline | timeline.setIntervals/setLanes/setBrush/setViewport/setWindow/setQueries/clearQueries/seek/play/pause/onChange, timeline.state, mountTimeline() — node/edge lifespans, labelled lanes, adaptive event/occupancy views, visual range selection, explicit filtering, playback, and settled UI state |
| Geo | createAtlasSource for validated, offline TopoJSON cartography; custom GeoReference; raster XYZ tiles; geo.enable; geo.vectors.set/fit/query/clear; geo.tiles.set/refresh/clear for GeoJSON or MVT sources; geo.heat; geo.clusters.enable/disable/clear; instance projection API |
| Contours | contours.set/show/hide/clear/visible — validated scalar grids, ordered line levels, shared camera, and PNG/PDF composition |
| Annotations | annotations.add/update/remove/toJSON/fromJSON/toSVG — text/rect/arrow/polygon/circle/callout; node circles and callouts follow layouts; optional @mizansec/ogjs/markup authoring |
| History | history.undo/redo (⌘Z/⇧⌘Z), atomic transactions, custom commands via history.run, and settled toolbar state through history.state / history.onChange |
| Selection | hitTest(screenX, screenY), setSelected, lasso.select() (freehand), getSelected, getVisible, clearSelection |
| Controls | mountHelm({ host?, searchText?, onChoose?, settings?, fullscreen?, grid?, cursor? }), unmountHelm(); settings, search, legend, progress, native fullscreen, cursor, and export-aware grid helpers |
| Export | Browser: export.png/svg/pdf/json/csv/graphml/gexf/xlsx(); PNG/PDF accept selection/rectangle crop, padding, and text watermark options. Node: new Graph(), new Camera(), and toSVG(graph, camera) without DOM or WebGL; legacy excel() is SpreadsheetML .xls |
| Events | nodehover/out/click/dblclick/activate, edgehover/out/click, drag, selectionchanged, click, viewchanged, animate, layoutstart/computed/end |
| Wrappers | BindingKit shared lifecycle; React (createOgjsComponent), Vue 3 (createOgjsVueComponent), Svelte (use:ogjs), and the Angular 21.2.20 AOT recipe in recipes/angular/ |
| Notebooks | @mizansec/ogjs/notebook versioned document bridge; separate ogjs-notebook anywidget package |
| Language bridge | One protocol-1 graph document from JavaScript/TypeScript, Python, Java, C#, Go, PHP, or Rust; five native recipes execute in verify:languages |
| Application adapters | Stable events and ids for app-owned collaboration, atomic edits for reviewed model results, and Strata surfaces for app-owned Canvas2D/PixiJS/Three.js/regl/MapLibre/Deck.gl runtimes |
import { BindingKit } from '@mizansec/ogjs';
const binding = new BindingKit({
nodes,
edges,
events: { nodeclick: (event, og) => inspect(og, event.nodeIndex) },
});
binding.mount(host);
binding.update({ nodes: nextNodes, edges: nextEdges });
binding.destroy();BindingKit owns the instance it creates. Pass instance instead of options
to bind an existing instance without transferring ownership. React, Vue, and
Svelte entry points use this same lifecycle, including event replacement,
option-driven recreation, SSR-safe construction, and deterministic cleanup.
The Angular 21.2.20 AOT recipe uses the root
BindingKit directly and is verified from the packed library by
npm run verify:angular. The consumer runs without Zone.js and proves
Angular-controlled teardown. Ogjs ships no Angular runtime entry point or
Angular dependency.
Collaboration services, identity, persistence, model inference, and third-party canvas engines remain application-owned. The application adapter lab shows the smallest supported seams: stable selection events, one validated undoable edit, and node-anchored risk annotations. Use Strata only when an application runtime genuinely needs its own canvas.
Jupyter and anywidget
The notebook bridge is a dedicated subpath, so importing the root package does not load notebook code:
import { NotebookPort } from '@mizansec/ogjs/notebook';
const port = new NotebookPort(host, { onEvent: (event) => publish(event) });
await port.update({
protocol: 1,
id: 'risk-notebook',
revision: 1,
graph: { nodes, edges }, // explicit stable node and edge ids
layout: { kind: 'hierarchical' },
fit: 'initial',
});
port.dispose();The separate Python package wraps the same document contract:
from ogjs_notebook import OgjsWidget
view = OgjsWidget({"nodes": nodes, "edges": edges}, port_id="risk-notebook")
view.layout("hierarchical", flow="south")
view.group_by("team")
viewGraph, layout, grouping, options, and standard node/edge style fields are validated before revision state changes. The wheel carries a local JavaScript bundle and makes no CDN request; notebook hosts and anywidget remain external dependencies.
og.layouts.hierarchical({
roots: ['gateway'],
flow: 'east',
components: 'across',
layerGap: 72,
nodeGap: 24,
componentGap: 48,
rankBy: ({ data }) => (data as { priority: number }).priority,
});rankBy runs once per node; lower finite numbers order nodes that share a
crossing-reduced sibling lane, so unrelated branches remain contiguous. The
callback must not change graph topology. Parents are centered over their
children with size-aware clearance. Use edge curvature for routed hierarchy
views.
Capability tracker: FEATURE-PARITY.md. Release and security processes: POLICY.md, SECURITY.md.
Derived projections
og.projections.defineNode('risk', ({ degree, data }) =>
(data as { base: number }).base + degree * 10);
og.projections.defineEdge('exposure', ({ sourceValue, targetValue, data }) =>
(sourceValue<number>('risk') ?? 0)
+ (targetValue<number>('risk') ?? 0)
+ (data as { amount: number }).amount);
og.transformations.addFilter(({ value }) => (value<number>('risk') ?? 0) >= 90);
og.styles.addNodeRule(({ value }) => ({
color: (value<number>('risk') ?? 0) >= 90 ? '#dc2626' : '#64748b',
}));
og.updateNode('account', { data: { base: 90 } });
og.projections.refresh(); // required after data or topology changesForce layout options
og.layouts.force({
iterations: 300,
balance: 'adaptive', // bounded tuning from graph size and mean degree
bodyMass: ({ degree }) => Math.max(1, degree),
linkPull: ({ data }) => typeof data === 'object' && data !== null && 'pull' in data
? Number(data.pull)
: 1,
repulsion: 1200, // many-body strength
springLength: 30, // edge rest length
springK: 0.05, // spring stiffness
gravity: 0.02, // pull toward origin
theta: 0.9, // Barnes-Hut opening angle
velocityDecay: 0.6, // friction
maxVelocity: 60, // per-tick displacement cap (stability)
sync: false, // true = run on main thread
});Callbacks run once on the calling thread and transfer compact numeric fields to the worker. Weighted accelerated requests use the worker fallback so their physics never silently changes.
Architecture
src/
core/Graph.ts SoA typed-array store, id→index map, dirty flags
render/Renderer.ts WebGL2 instanced passes, shared RGBA32F position texture
render/shaders.ts SDF-circle nodes, screen-space edge quads (GLSL 300 es)
render/Camera.ts pan/zoom/fit, world↔screen transforms
render/Labels.ts Canvas2D overlay with zoom LOD
layout/force.ts Barnes-Hut (flat-array quadtree), velocity-capped
layout/forceWorker.ts worker protocol: transferable frames
layout/LayoutManager.ts
analysis/ cancellable sync/worker graph insights
timeline/Timeline.ts dense intervals, filtering, playback, histogram
geo/ raster tiles, projection, GeoJSON vector layer
spatial/Grid.ts uniform hash grid: O(1) picking, viewport queries
style/Styles.ts rule-based styles compiled to typed-array writes
interaction/ navigation, picking, lasso, and Forge editing
demo/ Vite demo: configurable graph generator + FPS HUD
test/ deterministic unit and Playwright regression evidenceDevelopment
npm install
npm run dev # demo at http://localhost:5199
npm test # vitest suite
npm run typecheck
npm run build # ES module + .d.ts into dist/
npm run build:site # Cloudflare-ready static site into site-dist/
npm run evidence:portable # type/build/unit/browser push gate
npm run verify:consumer # pack, isolated install, type/build/browser smoke
npm run evidence:release # portable evidence followed by consumer verification
npm run evidence:performance # named compatible performance gate
# Repeated physical candidate/reference commands and verdict policy:
# docs/performance/CERTIFICATION.md
npm run evidence:visual # requires ignored licensed reference pixelsFor Cloudflare Pages, use npm run build:site as the build command and
site-dist as the output directory. The artifact includes the landing page,
documentation, demo bench, example catalog, and every linked example without
requiring a GitHub Actions workflow.
The repository-only consumer/investigation-desk/ is a focused cyber incident
application and permanent release gate. Its verifier copies the app to a clean
temporary directory and installs the generated tarball, never repository
source. It checks public exports and worker assets while exercising search,
selection, risk filters, timeline reduction, shortest-path analysis, atomic
containment and undo, JSON downloads, responsive layout, and automated WCAG
rules from the same package a customer receives.
Requirements
- WebGL2 (universal in 2026 browsers). No fallback renderer — Ogjs fails fast with a clear error instead of a slow canvas path.
- No runtime dependencies.
License
Commercial. Ogjs is proprietary software — see LICENSE.md. Evaluation use is permitted; production use requires a commercial license. Contact the maintainer for licensing terms.
