Problem
The force-directed graph never settles — nodes keep bouncing and jittering indefinitely. With 100+ entities the simulation runs hot, labels overlap, and the graph is hard to read. The physics simulation runs every frame with no cooling/convergence.
What's happening
simulate() runs on every requestAnimationFrame with constant repulsion/attraction
- No cooling factor — forces never decrease over time
- Nodes bounce off the viewport edges and ricochet back
- At 144 nodes / 378 edges (Clearview AI contract), it's visually chaotic
- Labels overlap badly when nodes are close
Fixes needed
Physics
- Add cooling — reduce force multiplier over time (alpha decay like d3-force). Start at 1.0, decay to 0 over ~300 frames. Reheat on drag/interaction.
- Increase min distance — nodes closer than 2x radius should get strong repulsion to prevent overlap
- Viewport-aware centering — auto-fit all nodes in view on initial load instead of random placement
Visual
- Label collision avoidance — don't render labels that would overlap. Show labels for selected node + neighbors, hide others until zoomed in.
- Adaptive label size — scale label font with zoom level. At zoom-out, hide labels entirely.
- Node size by degree — high-connection nodes should be larger (they're more important)
Performance
- Stop simulation when settled — if total velocity < threshold, stop calling simulate(). Resume on interaction.
- Spatial indexing — for 200+ nodes, O(n²) repulsion is slow. Consider Barnes-Hut approximation or quadtree.
- Canvas text caching — labels are expensive to render every frame
Context
45 nodes (Palantir) was fine. 144 nodes (Clearview AI) is at the edge. Larger investigations with 500+ entities will need these optimizations.
Problem
The force-directed graph never settles — nodes keep bouncing and jittering indefinitely. With 100+ entities the simulation runs hot, labels overlap, and the graph is hard to read. The physics simulation runs every frame with no cooling/convergence.
What's happening
simulate()runs on everyrequestAnimationFramewith constant repulsion/attractionFixes needed
Physics
Visual
Performance
Context
45 nodes (Palantir) was fine. 144 nodes (Clearview AI) is at the edge. Larger investigations with 500+ entities will need these optimizations.