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 });
<!doctype html> <html lang="en"><head><meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>Ogjs example — Topology lab</title> <link rel="stylesheet" href="../site.css"/> <style> #viz { position: fixed; inset: 52px 0 0 420px; } .panel { position: fixed; top: 66px; left: 14px; z-index: 5; width: 364px; background: #fff; border: 1px solid #dce3ec; border-radius: 12px; padding: 16px; box-shadow: 0 12px 30px rgba(15, 23, 42, .09); } .panel h1 { font: 700 18px var(--sans); color: #0f172a; margin: 0 0 5px; } .panel p { font: 400 13px/1.5 var(--sans); color: #526178; margin: 0; } .models { display: flex; flex-wrap: wrap; gap: 7px; margin: 14px 0 12px; } .models button { font: 650 12px var(--sans); color: #172033; background: #f8fafc; border: 1px solid #cbd5e1; border-radius: 7px; padding: 7px 10px; cursor: pointer; } .models button[aria-pressed="true"] { color: #fff; background: #172033; border-color: #172033; } #state { border-top: 1px solid #e5eaf1; padding-top: 11px; font: 600 12px/1.5 var(--mono); color: #526178; } @media (max-width: 720px) { .panel { top: 62px; left: 10px; width: calc(100vw - 20px); box-sizing: border-box; } #viz { inset: 315px 0 0 0; } } </style></head><body> <header class="rail"> <a class="wordmark" href="../index.html">OG<b>JS</b></a> <nav><a href="../docs.html">Docs</a><a href="../demos.html">Demos</a> <a href="./index.html" aria-current="page">Examples</a></nav> </header> <main> <div id="viz" aria-label="Generated topology"></div> <section class="panel" aria-labelledby="title"> <h1 id="title">Topology lab</h1> <p>Generate repeatable graph structures, then hand the result to any Ogjs layout.</p> <div class="models" aria-label="Topology model"> <button type="button" data-model="random-links">Random links</button> <button type="button" data-model="grid">Grid</button> <button type="button" data-model="tree">Tree</button> <button type="button" data-model="small-world">Small world</button> <button type="button" data-model="power-law">Power law</button> </div> <div id="state" aria-live="polite"></div> </section> </main> <script type="module"> // 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 }); </script> </body></html>