Tendril: I Built the Obsidian Graph View for the Web

The Problem

If you use Obsidian, you know the graph view.

That floating, organic visualization of your notes. Tags forming clusters. Ideas connecting across your vault. You can spend hours just… looking at it.

Then you try to publish your notes to the web.

The graph is gone. Or it’s static. Or it’s some sad approximation that doesn’t feel right.

I wanted my blog to feel like my Obsidian vault. So I built the graph myself.


The Journey

It started in April 2025. I was staring at ArgoBox (this blog) and something was missing.

My Obsidian vault had 181MB of notes. Thousands of connections. The graph view was addictive.

My blog was just… a list. Chronological. Linear. Boring.

So I started building.

The First Version

Cytoscape.js as the engine. Posts as blue nodes. Tags as green nodes. Force-directed layout.

It worked. But it felt dead.

Nodes settled too quickly. Clusters were too uniform. The whole thing had a mechanical quality that Obsidian’s graph never has.

The Physics Problem

I spent months tweaking parameters. Nothing felt right.

Then February 1, 2026. I decided to fix it once and for all.

Three hours later, I understood what was wrong: all my edges were the same length.

Obsidian’s graph has variable connections:

  • Spines (post to hub tag): Short, tight
  • Bridges (tag to tag): Long, loose

This creates the “sea urchin” geometry. Hubs with radiating satellites. Distant clusters connected by loose bridges.

// The fix
let isLeafConnection = sourceDegree <= 2 || targetDegree <= 2;
let length = isLeafConnection ? 5 : 150;  // Spines tight, Bridges loose

When I saw the result, I just stared at it. That was the moment it stopped looking like my graph and started looking like the graph.


What Tendril Does

Live physics controls. This is the big one. Nobody else has this.

Quartz, Obsidian Publish, MindStone - they all render static force-directed graphs. The physics runs once during generation.

Tendril’s physics run in the browser. In real-time. With sliders.

You can tune:

  • Center force (how strongly nodes pull toward center)
  • Repel force (how strongly nodes push apart)
  • Link force (how strongly connected nodes attract)
  • Damping (how quickly motion decays)

And watch the graph reorganize live.

Inline content preview. Click a node. A panel slides in showing the post excerpt, tags, date. No page navigation needed.

Framework agnostic. The core library is vanilla JavaScript. Use it with Astro, React, Vue, Svelte, whatever.

Smart labeling. Only hub nodes show labels. Reduces visual clutter without losing navigability.


The Open Source Decision

I could have kept this embedded in ArgoBox forever. A custom component for my personal blog.

But that felt wrong.

I’d spent months building something that solved a real problem - bringing Obsidian’s graph visualization to the web. Why should that stay private?

Tendril is MIT licensed. Free forever.

The repository: github.com/dlaforce/tendril (once published)


How It Compares

FeatureTendrilQuartzObsidian Publish
Live physics controlsYesNoNo
Inline content previewYesNoSeparate pane
Framework agnosticYesNo (Hugo)N/A
Customizable forcesYesNoNo
Open sourceYes (MIT)Yes (MIT)No
CostFreeFree$8/month

Tendril isn’t trying to replace Quartz. If you need backlinks, wikilinks, and full Obsidian-to-web publishing, Quartz is probably better.

But if you want the graph experience - the floaty, organic, Obsidian feel - that’s what Tendril does.


The Technical Details

Physics Parameters

const graph = new TendrilGraph('#container', {
  nodes: [...],
  edges: [...],
  physics: {
    centerForce: 0.05,   // Low - allows organic spread
    repelForce: 4000,    // High - cluster separation
    linkForce: 1.0,      // Strong - tight connections
    linkDistance: 40,    // Short base (spines override)
    damping: 0.85        // Smooth settling
  }
});

Spine/Bridge Classification

// Edges between highly-connected nodes = bridges (long, loose)
// Edges involving leaf nodes = spines (short, tight)
const edgeData = edges.map(edge => {
  const sourceDeg = degreeMap.get(edge.source);
  const targetDeg = degreeMap.get(edge.target);

  const isLeaf = sourceDeg <= 2 || targetDeg <= 2;

  return {
    ...edge,
    length: isLeaf ? spineLength : bridgeLength,
    weight: isLeaf ? 1.5 : 0.1
  };
});

Tag Hierarchy

Automatic parent-child relationships between tags:

const TAG_HIERARCHY = {
  'gentoo': 'linux',
  'k3s': 'kubernetes',
  'kubernetes': 'containers',
  'docker': 'containers'
};

This creates structural backbone. gentoo connects to linux, which connects to other distro tags.


The ArgoBox Connection

This blog uses Tendril. The graph you see (if you haven’t disabled it) is the same library.

ArgoBox is designed to feel like an Obsidian vault. The dark theme. The interconnected content. The graph view.

If you’re an Obsidian user, you might appreciate that. If you’re not… well, the blog still works like a normal blog.


What’s Next

The roadmap:

  1. Publish to npm - npm install @tendril/graph
  2. Astro integration - npm install @tendril/astro
  3. CLI template - npx create-tendril my-blog
  4. Documentation site - usetendril.com

Eventually: backlinks, wikilinks parsing, search integration.

But the core is done. The physics feel right. The graph is alive.


Try It

If you want to add an Obsidian-style graph to your site:

  1. Check the template at github.com/dlaforce/tendril
  2. Or use the core library directly with @tendril/graph

MIT license. No strings attached.


The Lesson

Sometimes you spend months building something that “works” but doesn’t feel right.

Sometimes you need a 3-hour session to figure out why.

Variable edge lengths. That was the secret. Spines and bridges.

Now when I look at the graph on this site, it feels like my Obsidian vault. That’s all I wanted.

“Ideas that grow and connect.”

That’s Tendril. That’s what it does.