Graph Timelapse · spin-off library · live demo
steady-graph
Force-directed graphs start from random positions, so the same notes land somewhere new every time you open them: spun, flipped and shuffled. Play a month of them back to back and you get a blender. steady-graph is the 150 lines inside Graph Timelapse that fix that, pulled out so anyone can use them.
← Graph Timelapse · steady-graph on GitHub · MIT, no dependencies
align()Rotate, flip and shift each day to match the last. Shape untouched. Movement: –steady()Align, then let notes drift instead of jump; new notes appear beside what they link to. Movement: –
“Movement” is how far notes that already existed move from one day to the next, on average, compared with the recorded layouts. The vault is made up: five topics, a few new notes a day, each linked to busy notes in its topic. The layouts come from a small force simulation running in your browser right now.
How it works
1. Line the shapes up. Take the notes that exist on both days. Subtract each day’s centre, and the only difference left is a rotation, maybe a flip. Add up, over every note, how far it would need to turn (a cross product) and how much it already agrees (a dot product). The best angle is atan2(turn, agree). That’s 2D Procrustes analysis: one pass, no matrices. Try it flipped too and keep whichever fits better.
2. Ease off. Rigid alignment can’t stop the layout moving a whole cluster across the graph. So each existing note only moves part of the way toward its new spot per frame (30% by default), and a brand-new note is placed relative to the notes it links to, wherever those are drawn right now.
Use it
npm install steady-graph
import { steady } from "steady-graph";
let shown = null; // what's on screen
function onNewLayout(positions, links) { // Map(id -> {x, y}), [[idA, idB], ...]
shown = steady(shown, positions, links, { amount: 0.7 });
draw(shown);
}
Works with any layout engine: d3-force, Cytoscape, Gephi exports, Obsidian. Full docs and the math in plain words are in the README.