OG
JS
Docs
Demos
Examples
Topology lab
Generate repeatable graph structures, then hand the result to any Ogjs layout.
Random links
Grid
Tree
Small world
Power law
// A topology recipe declares model, scale, and seed and returns ordinary graph data. // Any OGJS layout can consume the result; generation stays deterministic without embedding // one fixed showcase graph or canvas geometry. import { Ogjs, synthesizeTopology } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light' }); const recipes = { 'random-links': { model: 'random-links', nodes: 36, edges: 72, seed: 19, span: 440 }, grid: { model: 'grid', rows: 5, columns: 7, spacing: 64 }, tree: { model: 'tree', nodes: 40, children: 3, spacing: 70 }, 'small-world': { model: 'small-world', nodes: 30, neighbors: 4, rewire: .18 }, 'power-law': { model: 'power-law', nodes: 60, attachments: 2, seed: 23 }, }; // Recipes contain model parameters only—scale, connectivity, and repeatability—not display size // or canvas coordinates. `synthesizeTopology` returns ordinary graph data for any layout. const modelColor = { 'random-links': '#2563eb', grid: '#0d9488', tree: '#7c3aed', 'small-world': '#0891b2', 'power-law': '#2563eb', }; function show(model) { const draft = synthesizeTopology(recipes[model]); // Degree is derived after generation to annotate influential nodes; it does not alter the // synthesized topology or become a hidden generator input. const degree = new Map(draft.nodes.map((node) => [node.id, 0])); for (const edge of draft.edges) { degree.set(edge.source, degree.get(edge.source) + 1); degree.set(edge.target, degree.get(edge.target) + 1); } const leaders = new Set([...degree.entries()] .sort((a, b) => b[1] - a[1] || String(a[0]).localeCompare(String(b[0]))) .slice(0, 4).map(([id]) => id)); const nodes = draft.nodes.map((node) => ({ ...node, size: Math.min(10, 4 + Math.sqrt(degree.get(node.id)) * .7), color: model === 'power-law' && leaders.has(node.id) ? '#d97706' : modelColor[model], label: leaders.has(node.id) ? `${node.id} · ${degree.get(node.id)}` : undefined, })); og.setGraph({ nodes, edges: draft.edges }); if (model === 'power-law') { // The generator describes relationships, while layout decides presentation. Only the irregular // power-law fixture needs force placement; grid and tree recipes already include coordinates. void og.layouts.force({ sync: true, iterations: 260, springLength: 48, repulsion: 1800, }); } og.camera.fit(); document.querySelector('#state').textContent = `${draft.model} · ${draft.nodes.length} nodes · ${draft.edges.length} edges · seed ${draft.seed}`; for (const button of document.querySelectorAll('[data-model]')) { button.setAttribute('aria-pressed', String(button.dataset.model === model)); } } for (const button of document.querySelectorAll('[data-model]')) { button.addEventListener('click', () => show(button.dataset.model)); } show('small-world'); // Destroy the instance when the example leaves the page so worker, renderer, and event resources // are released together. addEventListener('pagehide', () => og.destroy(), { once: true });