An Interactive Explorer
In this tutorial you’ll build a compact graph explorer on top of a larger generated dataset. By the end you’ll have hover highlighting, click-to-select with a rectangle tool, the ability to focus the camera on any node, one-click neighbor expansion, and a search box that filters and locates matching nodes.
Each section builds on the last, and the full combined script
is at the end. It assumes you’ve installed @kortexya/nodus
and read Your First Graph.
1. Container and search box
Add a container plus a small search input. The CSS below positions the search box over the graph; you only need this HTML and CSS once.
<div id="graph"></div><input id="search" type="text" placeholder="Search nodes…" autocomplete="off" />
<style> html, body { margin: 0; height: 100%; } #graph { width: 100vw; height: 100vh; } #search { position: fixed; top: 16px; left: 16px; z-index: 10; padding: 8px 12px; font-size: 14px; border-radius: 8px; border: 1px solid #cbd5e1; background: #ffffff; }</style>2. Create the instance and generate a graph
Construct Nodus, then fabricate a larger graph with one of the seeded
generators. barabasiAlbert builds a
scale-free network — a realistic shape for an explorer, with a few high-degree
hubs and many leaf nodes. Generators are deterministic and return a Promise.
import { Nodus } from '@kortexya/nodus';
const nodus = new Nodus({ container: document.getElementById('graph') });
// Fabricate a scale-free graph to explore — generators return the graph.const g = await nodus.generate.barabasiAlbert({ nodes: 40, m0: 2, m: 1 });await nodus.setGraph(g);3. Style, lay out and frame
Give nodes and edges a base style with rules, then apply a
layout and fit the graph to the viewport. The force layout suits an organic
network like this one; for a tree-shaped dataset you’d reach for
nodus.layouts.hierarchical instead.
nodus.styles.addNodeRule({ color: '#334155', radius: 10, shape: 'circle',});nodus.styles.addEdgeRule({ color: '#cbd5e1', width: 1.5,});
await nodus.layouts.force({ duration: 0 });await nodus.view.locateGraph();4. Hover highlighting
styles.setHoveredNodeAttributes defines the visual attributes a node takes on
while the pointer is over it. It applies globally — no per-node wiring needed.
nodus.styles.setHoveredNodeAttributes({ color: '#0ea5e9', radius: 14, outline: { color: '#0ea5e9' },});Now hovering any node enlarges it and gives it a bright outline, then reverts automatically when the pointer leaves.
5. Click-to-select with the rectangle tool
Enable the rectangleSelect tool so dragging a box over the canvas selects the
nodes inside it. Read the current selection back with getSelectedNodes, which
returns a NodeList. You can also style the selected state with
styles.setSelectedNodeAttributes.
nodus.tools.rectangleSelect.enable();
nodus.styles.setSelectedNodeAttributes({ color: '#f59e0b', outline: { color: '#b45309' },});
// Log the selection whenever the user finishes a box.nodus.tools.tooltip.onBackgroundClick(() => { const selected = nodus.getSelectedNodes(); console.log(`${selected.size} node(s) selected`);});getNonSelectedNodes, clearSelection and the per-element node.setSelected(bool)
round out the selection API.
6. Focus the camera on a node
When the user clicks a node, focus on it. node.locate() pans and zooms the
camera to that single element; pass a
{ duration } to animate the move.
nodus.tools.tooltip.onNodeClick((node) => { node.locate({ duration: 400 });});7. Neighbor expansion
Highlighting a node’s neighborhood is a common explorer gesture. On
double-click, read the node’s neighbors with node.getAdjacentNodes() — which
returns a NodeList — and pulse them so they stand out.
nodus.tools.tooltip.onNodeDoubleClick((node) => { const neighbors = node.getAdjacentNodes(); neighbors.pulse(); console.log(`${node.getId()} has ${neighbors.size} neighbor(s)`);});pulse is a broadcast method on a NodeList: it runs on every node in the
collection. You could equally neighbors.setSelected(true) or
neighbors.addClass('neighborhood') to mark them.
8. A live search filter
Wire the search box to filter nodes and frame the matches. getNodes(predicate)
returns a NodeList of nodes for which the predicate is true; calling
.locate() on that list pans and zooms the camera to fit all of them.
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', (event) => { const query = event.target.value.trim().toLowerCase(); if (!query) return;
const matches = nodus.getNodes( (node) => String(node.getId()).toLowerCase().includes(query), );
if (matches.size > 0) { matches.setSelected(true); matches.locate({ duration: 400 }); }});If your nodes carry a label under data, match on node.getData().label
instead of the id.
9. The full script
Everything from steps 2–8, combined. Pair it with the container and search-box HTML and CSS from step 1.
import { Nodus } from '@kortexya/nodus';
// 2. Create and generate.const nodus = new Nodus({ container: document.getElementById('graph') });const g = await nodus.generate.barabasiAlbert({ nodes: 40, m0: 2, m: 1 });await nodus.setGraph(g);
// 3. Base style, layout, frame.nodus.styles.addNodeRule({ color: '#334155', radius: 10, shape: 'circle' });nodus.styles.addEdgeRule({ color: '#cbd5e1', width: 1.5 });await nodus.layouts.force({ duration: 0 });await nodus.view.locateGraph();
// 4. Hover highlighting.nodus.styles.setHoveredNodeAttributes({ color: '#0ea5e9', radius: 14, outline: { color: '#0ea5e9' },});
// 5. Rectangle selection.nodus.tools.rectangleSelect.enable();nodus.styles.setSelectedNodeAttributes({ color: '#f59e0b', outline: { color: '#b45309' },});nodus.tools.tooltip.onBackgroundClick(() => { console.log(`${nodus.getSelectedNodes().size} node(s) selected`);});
// 6. Focus on click.nodus.tools.tooltip.onNodeClick((node) => node.locate({ duration: 400 }));
// 7. Neighbor expansion on double-click.nodus.tools.tooltip.onNodeDoubleClick((node) => { const neighbors = node.getAdjacentNodes(); neighbors.pulse();});
// 8. Live search.document.getElementById('search').addEventListener('input', (event) => { const query = event.target.value.trim().toLowerCase(); if (!query) return; const matches = nodus.getNodes( (node) => String(node.getId()).toLowerCase().includes(query), ); if (matches.size > 0) { matches.setSelected(true); matches.locate({ duration: 400 }); }});Next steps
- Interaction Tools — every tool and its handlers.
- Selection — the full selection model.
- Controlling the Camera — zoom, pan, fit and locate.
- Generating Graphs — all eight generators.
- A Co-author Hypergraph — the hypergraph pipeline.