A Co-author Hypergraph
A hypergraph lets a single relationship connect any number of entities at once. Co-authorship is a natural fit: each author is a vertex, and each paper is a hyperedge incident to all of its authors. A paper with four co-authors is one hyperedge over four vertices — something a plain edge can’t express.
This tutorial runs the full hypergraph pipeline on a small co-authorship dataset: build the data, simplify it, lay it out, render it, inspect quality metrics, and react to clicks. The full combined script is at the end.
Nodus’s hypergraph subsystem implements the method of Oliver et al., “Scalable Hypergraph Visualization” (TVCG 2023).
1. Container
A standard container with a size; you only need this HTML and CSS once.
<div id="graph"></div>
<style> html, body { margin: 0; height: 100%; } #graph { width: 100vw; height: 100vh; }</style>2. Create the instance and build the data
Construct Nodus as usual, then assemble a HypergraphData object. It has two
arrays: vertices (the authors) and hyperedges (the papers). Each hyperedge
lists the ids of the vertices it’s incident to.
import { Nodus } from '@kortexya/nodus';
const nodus = new Nodus({ container: document.getElementById('graph') });
const data = { vertices: [ { id: 'alice', data: { name: 'Alice' } }, { id: 'bob', data: { name: 'Bob' } }, { id: 'carol', data: { name: 'Carol' } }, { id: 'dave', data: { name: 'Dave' } }, { id: 'erin', data: { name: 'Erin' } }, { id: 'frank', data: { name: 'Frank' } }, ], hyperedges: [ { id: 'p1', vertices: ['alice', 'bob', 'carol'], data: { title: 'On Hypergraphs' } }, { id: 'p2', vertices: ['bob', 'dave'], data: { title: 'Layout Methods' } }, { id: 'p3', vertices: ['carol', 'dave', 'erin'], data: { title: 'Simplification' } }, { id: 'p4', vertices: ['erin', 'frank'], data: { title: 'Rendering Sets' } }, { id: 'p5', vertices: ['alice', 'dave', 'frank'],data: { title: 'Quality Metrics' } }, ],};Each vertex may optionally carry a nodeId to bind it to an existing graph
node; here we keep the hypergraph standalone.
3. Set the data
Hand the data to the hypergraph subsystem. setData is chainable — it returns
the hypergraph object — but we’ll keep the steps separate for clarity.
nodus.hypergraph.setData(data);4. Simplify
Real co-authorship hypergraphs are dense and overlapping. simplify reduces
them while preserving structure, controlled by the alpha, beta and gamma
weights and a stopping condition. stopWhen decides when to halt — here we stop
once no forbidden subgraphs remain.
nodus.hypergraph.simplify({ alpha: 1.0, beta: 1.0, gamma: 1.0, stopWhen: 'noForbidden',});simplify returns a { scales, operations } summary describing the
multi-scale reduction it performed.
5. Lay out
layout positions the vertices and shapes the hyperedge regions. It’s async and
resolves to { totalEnergy, overlapCount } — a quick read on how well the
layout converged. Tune the per-phase iteration counts and pick a solver
('lbfgs' or 'adam'); the onProgress callback reports intermediate state.
const result = await nodus.hypergraph.layout({ separationIters: 200, regularityIters: 200, solver: 'lbfgs', onProgress: (info) => console.log('layout progress', info),});
console.log('total energy:', result.totalEnergy);console.log('overlaps:', result.overlapCount);6. Render
render draws the result. view: 'primal' shows the author vertices wrapped by
their paper regions. palette colors the hyperedge regions, and showVertices
toggles the vertex markers.
nodus.hypergraph.render({ view: 'primal', palette: ['#0ea5e9', '#f59e0b', '#10b981', '#a855f7', '#ef4444'], showVertices: true,});render returns a { primal?, dual? } object with handles to the produced
views. Switch views later with nodus.hypergraph.setActiveView('dual'), or
render view: 'both' to see the primal and dual side by side.
7. Read quality metrics and handle clicks
getQualityMetrics reports how clean the layout is — overlap count and area,
average regularity, and how many forbidden subgraphs remain. Use it to decide
whether to re-run simplify or layout with different weights.
Wire interaction with nodus.hypergraph.on(event, cb). The hyperedgeClick
event fires with the clicked hyperedge, so you can surface the paper’s title.
const metrics = nodus.hypergraph.getQualityMetrics();console.log('overlap count:', metrics.overlapCount);console.log('avg regularity:', metrics.avgRegularity);console.log('forbidden subgraphs:', metrics.forbiddenSubgraphCount);
nodus.hypergraph.on('hyperedgeClick', (hyperedge) => { console.log('clicked paper', hyperedge);});8. The full script
Steps 2–7 combined. Pair it with the container HTML and CSS from step 1.
import { Nodus } from '@kortexya/nodus';
// 2. Create and build data.const nodus = new Nodus({ container: document.getElementById('graph') });
const data = { vertices: [ { id: 'alice', data: { name: 'Alice' } }, { id: 'bob', data: { name: 'Bob' } }, { id: 'carol', data: { name: 'Carol' } }, { id: 'dave', data: { name: 'Dave' } }, { id: 'erin', data: { name: 'Erin' } }, { id: 'frank', data: { name: 'Frank' } }, ], hyperedges: [ { id: 'p1', vertices: ['alice', 'bob', 'carol'], data: { title: 'On Hypergraphs' } }, { id: 'p2', vertices: ['bob', 'dave'], data: { title: 'Layout Methods' } }, { id: 'p3', vertices: ['carol', 'dave', 'erin'], data: { title: 'Simplification' } }, { id: 'p4', vertices: ['erin', 'frank'], data: { title: 'Rendering Sets' } }, { id: 'p5', vertices: ['alice', 'dave', 'frank'], data: { title: 'Quality Metrics' } }, ],};
// 3–4. Set data and simplify.nodus.hypergraph.setData(data);nodus.hypergraph.simplify({ alpha: 1.0, beta: 1.0, gamma: 1.0, stopWhen: 'noForbidden' });
// 5. Lay out.const result = await nodus.hypergraph.layout({ separationIters: 200, regularityIters: 200, solver: 'lbfgs', onProgress: (info) => console.log('layout progress', info),});console.log('energy', result.totalEnergy, 'overlaps', result.overlapCount);
// 6. Render.nodus.hypergraph.render({ view: 'primal', palette: ['#0ea5e9', '#f59e0b', '#10b981', '#a855f7', '#ef4444'], showVertices: true,});
// 7. Metrics and clicks.const metrics = nodus.hypergraph.getQualityMetrics();console.log('overlaps', metrics.overlapCount, 'regularity', metrics.avgRegularity);
nodus.hypergraph.on('hyperedgeClick', (hyperedge) => { console.log('clicked paper', hyperedge);});The pipeline is setData → simplify → layout → render. Re-run simplify or
layout with different weights and iteration counts, then call render again
to compare.
Next steps
- Hypergraphs — the model and the pipeline in depth.
- Hypergraph Visualization — every option, view and event.
- An Interactive Explorer — interaction patterns on ordinary graphs.