Skip to content
← writing

Nexus Score v2: Teaching Notes to Know Their Place

In my original Nexus Score post I described a system for scoring notes based on link counts and word length. It worked, sort of. But I was never fully satisfied with it. The scores felt arbitrary, the topology classification was too simplistic, and the whole thing couldn’t account for incoming links because of how remark plugins work.

So I threw it all out and rebuilt it from scratch using actual graph theory. Here’s how Nexus Score v2 works.

What Was Wrong With v1

The original system had a few fundamental problems that kept nagging at me:

The Graph Approach

The fix was to stop thinking about notes as isolated documents and start treating them as nodes in a directed graph. I’m using Graphology to build the graph at build time, which gives me access to proper graph algorithms rather than hand-rolled arithmetic.

const graph = new Graph({ type: "directed", multi: false });

// Every post becomes a node
for (const post of allPosts) {
  graph.addNode(`${post.collection}/${post.id}`, {
    title, collection, id, wordCount, externalLinkCount: 0, tags,
  });
}

// Wikilinks become directed edges
const wikilinks = body.match(/(?<=\[\[)(.*?)(?=\]\])/g) ?? [];
for (const raw of wikilinks) {
  const targetNodeId = resolveTarget(normalizeWikilink(raw), idToNode);
  if (targetNodeId && targetNodeId !== sourceId && !graph.hasEdge(sourceId, targetNodeId)) {
    graph.addEdge(sourceId, targetNodeId);
  }
}

By building the full graph before computing any scores, the incoming links problem goes away entirely. Every node knows its in-degree because the graph tracks all edges bidirectionally.

Six Algorithms, One Cache

Once the graph is built I run six algorithms across all nodes and cache the results. This is the expensive part, so it only runs once per build.

cachedBetweenness = betweennessCentrality(graph);
cachedPageRank = pagerank(graph, { getEdgeWeight: null });
cachedCloseness = closenessCentrality(graph, { wassermanFaust: true });
cachedHITS = hits(graph, { normalize: true });

On top of these I also run Louvain community detection to identify clusters of related notes, and Breadth-First Search (BFS) from every node to compute eccentricity and reachability. 1 Community clusters derived from link structure could eventually augment or even replace manually assigned tags as a way of organizing the garden.

Here’s what each algorithm contributes:

Five Topology Roles

The old system had three topology types based on link direction ratios. The new system has five, classified using the normalized algorithm outputs:

let topology: "B" | "A" | "H" | "R" | "T" = "T";

if (betweennessN > 0.3 && (inDeg + outDeg) > 2) {
  topology = "B"; // Bridge: structural connector between clusters
} else if (authorityN > 0.3 && inDeg >= outDeg && inDeg > 0) {
  topology = "A"; // Authority: well-referenced canonical note
} else if (hubN > 0.3 && outDeg > inDeg) {
  topology = "H"; // Hub: curates and links to authorities
} else if (reachability > 0.4 && reciprocity >= 1) {
  topology = "R"; // Relay: well-integrated, bidirectional exchange
}
// else T (Terminal): leaf or low-connectivity note

Four-Dimensional Scoring

Instead of a single weighted sum, Nexus Score v2 uses four normalized dimensions, each capturing a different aspect of a note’s character:

// Substance: content depth (25%)
const substance = 0.7 * (wordCount / 3000) + 0.3 * (externalLinks / 8);

// Position: structural importance (30%)
const position = 0.4 * betweennessNorm + 0.3 * pageRankNorm + 0.3 * closenessNorm;

// Integration: embeddedness (25%)
const integration = 0.6 * reachability + 0.4 * (reciprocity / 5);

// Authority: recognition (20%)
const authority = 0.5 * authorityNorm + 0.5 * inDegreeNorm;

const composite = 0.25 * substance + 0.30 * position + 0.25 * integration + 0.20 * authority;

Position gets the highest weight because I care most about how a note fits into the broader structure of the garden. Substance and Integration share second place. Authority gets the lowest weight because I don’t want notes to score highly just because they happen to be linked to a lot; they need to earn it through structural importance and content depth too.

Each dimension is normalized to 0–1 using the graph’s own maximums as the ceiling. This means scores are relative to the garden’s current state rather than against arbitrary thresholds.

The composite score maps to a maturity stage:

CompositeStage
< 0.12Fragment
< 0.25Basic
< 0.45Developed
< 0.65Advanced
≥ 0.65Integrated

The final Nexus Score is a combination of topology and stage, e.g., B_Advanced or T_Fragment.

Chladni Patterns: A New Visual Language

The original Nexus Score used Tabler SVG icons to represent scores. They worked but they felt arbitrary. There was no visual logic connecting a topology-ring icon to the concept of a hub note.

For v2 I replaced them with something I find far more satisfying: ASCII patterns inspired by Chladni figures. These are the patterns that emerge when you vibrate a metal plate covered in sand. Different resonant frequencies produce different nodal line patterns, and they happen to map beautifully to the concept of increasing connectivity and complexity.

Each Nexus Score is encoded as a 3×3 dot grid. The spatial pattern of “active” cells encodes the topology role, while the character weight encodes the maturity stage.

Role masks (increasing nodal-line complexity):

T (Terminal)    R (Relay)      B (Bridge)     A (Authority)   H (Hub)
· · ·           · · ·          • · •          · • ·           • • •
· • ·           • • •          · • ·          • • •           • • •
· · ·           · · ·          • · •          · • ·           • • •

For stage encoding, I use a character cascade where each stage’s active character becomes the next stage’s inactive character. This creates a visual progression of increasing intricacy:

const CHAR_PAIRS: [active, inactive][] = [
  ["·", "·"],   // Fragment    — uniform, undifferentiated
  ["•", "·"],   // Basic       — first contrast appears
  ["○", "•"],   // Developed   — hollow forms emerge
  ["●", "○"],   // Advanced    — solid forms dominate
  ["◆", "●"],   // Integrated  — diamond apex
];

A T_Fragment is just nine identical dots. An A_Integrated is a cardinal cross of diamonds against a field of filled circles. The visual complexity grows with the note’s actual complexity. I like that.

Garden-Wide Stats

With a proper graph in place I could also compute aggregate stats for the garden as a whole. Things like graph density, diameter (longest shortest path), community count, orphan nodes, and mutual link counts.

These get surfaced on the notes listing page so I can keep an eye on the health of the garden at a glance. A high orphan count tells me I have disconnected notes that need linking. A low density tells me there’s room for more cross-pollination between topics.

Was It Worth It?

The original Nexus Score was a weekend project driven by curiosity. The v2 rebuild was a much larger effort, but it gave me something the original never could: scores that actually mean something.

When I see a note classified as a Bridge, I know it genuinely sits between two clusters of ideas. When a note reaches Integrated status, it’s because it’s deeply embedded in the garden’s structure, not because it happens to be long.

There are still improvements to be made. The stage thresholds are still somewhat arbitrary, and the dimension weights could use more tuning as the garden grows. But the foundation is solid now, and the scores tell me things about my notes that I wouldn’t have noticed otherwise.

If you’re curious about how a specific note scores, click on the [+] icon at the top of any note to expand the full metrics panel. It shows all fourteen metrics across five rows, everything from PageRank to community labels.


Future Considerations


Next → You Have to Care
AI was used for researching information, proofreading and grammar correction, code assistance, and system design and architecture in the creation of the content on this page. Policy.