Decorative live WebGL2 render of a 40,000 node graph drawn as an elliptical disc, turning slowly on its axis. It demonstrates the renderer, not the force solver.
Keep the graph on the GPU.
Render, lay out, edit, and analyze large networks in the browser without blocking the interface.
Numbers with a test attached.
Every report records the workload, seed, browser, GPU, viewport, and timing method. Runs compare only against compatible profiles, and the gate rejects regressions.
SwiftShader implements the GPU on the CPU. This profile is a regression gate, not a hardware speed claim. Your device will differ; measure it.
One position texture. Two readers.
Every node and edge attribute lives in a flat typed array. No per-element objects, no GC pressure, one contiguous upload path.
Node positions live in a single RGBA32F texture. It is the one place layout and rendering agree on.
Two writers. The GPU force pass writes positions inside a fragment shader. The worker force streams transferable frames, uploaded with one texSubImage2D per frame regardless of edge count.
Two readers. The node and edge shaders sample the same texture with texelFetch, so the simulation writes positions where the renderer already reads them.
A Canvas2D overlay composites labels with zoom level-of-detail over the WebGL2 output. Overlays stay crisp; the graph body stays instanced.
id → index map
Scroll to step through the pipeline · terminology from src/render and src/layout
Edit the graph without losing state.
Try it here: lasso a cluster, group it, slide the time window, then undo the whole edit. Every action below runs live in this page.
The shipped API behind each action: lasso.select() · og.groupVisually() / ungroup · timeline.setWindow() · history.undo() / redo()
Eight subsystems, one typed-array core.
Rendering and styling
Rule-based styles are evaluated once and compiled into typed-array writes.
styles.addNodeRule(fn)
A small surface for a large graph.
import { Ogjs } from '@mizansec/ogjs';
const og = new Ogjs({ container: '#viz' });
og.addNodes([{ id: 'a', label: 'Alice', size: 8 }, { id: 'b', label: 'Bob' }]);
og.addEdges([{ source: 'a', target: 'b' }]);
// worker-based force layout: streams frames, never blocks the UI
await og.layouts.force({ iterations: 300 });
og.camera.fit();
import React from 'react';
import { createOgjsComponent } from '@mizansec/ogjs/react';
// React is injected, so the package keeps zero dependencies
const OgjsGraph = createOgjsComponent(React);
<OgjsGraph nodes={nodes} edges={edges}
onReady={(og) => og.layouts.forceGPU()}
style={{ width: '100%', height: 400 }} />
import * as Vue from 'vue';
import { createOgjsVueComponent } from '@mizansec/ogjs/vue';
const OgjsGraph = createOgjsVueComponent(Vue);
<!-- emits 'ready' with the Ogjs instance -->
<OgjsGraph :nodes="nodes" :edges="edges" @ready="onReady" />
<script>
import { ogjs } from '@mizansec/ogjs/svelte';
</script>
<!-- an action, stable across Svelte 4 and 5 -->
<div use:ogjs={{ nodes, edges, onReady }} style="width:100%;height:400px" />
- RUNTIME DEPENDENCIES
- 0
- MODULE FORMAT
- ES module with TypeScript declarations
- WORKER ASSETS
- Ship inside the package. Every release is verified by installing the packed tarball into an isolated consumer app.
- ENTRYPOINTS
- @mizansec/ogjs
@mizansec/ogjs/react
@mizansec/ogjs/vue
@mizansec/ogjs/svelte - REQUIREMENT
- WebGL2. Ogjs fails fast with a clear error instead of a slow canvas path.
EVALUATION / COMMERCIAL LICENSE
Put your own data on the canvas.
Ogjs is commercial software. Evaluation use is permitted, with your own graph data. Production use requires a commercial license.
info@mizansec.com