npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jonobr1/force-directed-graph

v1.24.3

Published

GPU supercharged attraction-graph visualizations for the web built on top of Three.js

Readme

Force Directed Graph

GPU supercharged attraction-graph visualizations for the web built on top of Three.js. Importable as an ES6 module.

  1. 🧮 Simulation computed on GPU via render targets
  2. 🕸️ Accepts thousands of nodes and links
  3. 🎨 Configurable point and link colors
  4. 〰️ GPU-expanded antialiased line rendering with configurable width
  5. 📦 Single library dependent (Three.js)
  6. 🧩 Three.js scene compatible object
  7. 📝 Simple data schema to populate compatible with d3.js JSON samples
  8. 🧊 2d & 3d simulation modes
  9. 🦺 WASM workers to generate textures

Visit the hosted project page for a running demo.

Usage

npm install --save three @jonobr1/force-directed-graph

Import in ES6 environment

import { ForceDirectedGraph } from '@jonobr1/force-directed-graph';

Data Schema (constructor and set)

Reference: The accepted nodes / links structure is inspired by the D3 force-directed graph data format: @d3/force-directed-graph-component.

The same data object shape is accepted by:

  • new ForceDirectedGraph(renderer, data)
  • fdg.set(data[, callback])

[!TIP] It is recommended to use set and update setters in the callback or after the promise resolves. E.g: fdg.set(data).then(() => {fdg.linewidth = 2})

type GraphData = {
  nodes: NodeData[];
  links: LinkData[];
};

type NodeData = {
  id: string | number; // Required, unique per node
  x?: number;          // Optional initial / target x position
  y?: number;          // Optional initial / target y position
  z?: number;          // Optional initial / target z position
  isStatic?: boolean;  // Optional, pins node when true
  color?: THREE.ColorRepresentation; // Optional Three.js color input
  image?: string | HTMLImageElement; // Optional image URL or image element
  label?: string | number; // Optional canvas-atlas text label
  labelPriority?: number; // Optional label ranking override
  size?: number        // Optional size for per-node sizing
};

type LinkData = {
  source: string | number; // Node reference (must match a node.id)
  target: string | number; // Node reference (must match a node.id)
};

[!NOTE]

  • nodes and links are both required.
  • source / target are resolved by node id.
  • If x, y, or z is omitted, a random initial position is assigned.
  • If a node defines at least two of x, y, z, those values also become that node's target position when pinStrength > 0.
  • isStatic defaults to false.
  • If color is omitted, the node defaults to white.
  • set(data[, callback]) returns a Promise that resolves when geometry/textures are ready.
  • obscurity is label-density control: 0 shows all labels, 0.75 targets roughly 25% visible labels, and 1 hides all labels. The active subset is now chosen from graph topology and priority, not camera clipspace placement.
  • fdg.labels.alignment ('center' | 'left' | 'right') and fdg.labels.baseline ('top' | 'middle' | 'bottom') change label anchoring live.
  • fdg.labels.offset (THREE.Vector2) adds extra label padding in label-space x/y.
  • fdg.labels.near (camera-space distance, default 0) discards labels at or closer than that depth, which is useful when sizeAttenuation makes nearby labels too large.
  • fdg.labelsInheritColor toggles whether labels use each node's color, and fdg.labelColor tints all labels uniformly on top of the white label atlas.
  • fdg.labels.fontSize scales the rendered label planes without rebuilding the atlas; fdg.labels.fontFamily rebuilds the atlas with a new CSS font stack.
  • fdg.refreshLabels() rebuilds label atlas data after mutating node labels, priorities, sizes, or colors in-place.
  • fdg.getPerformanceInfo(), fdg.isWorkerProcessingAvailable(), and fdg.isWasmAccelerationAvailable() expose worker / WASM capability state.

Selected Instance API

  • fdg.pinStrength: controls how strongly nodes are attracted toward their target positions derived from x, y, z.
  • fdg.refreshLabels(): reparses labels from current node data and updates or removes the labels mesh as needed.
  • fdg.labels: returns the labels mesh when labels exist, otherwise null.
  • fdg.getPerformanceInfo(): returns { workerSupported, workerReady, wasmReady, pendingRequests }.

Load Script in HTML file:

This example creates 512 nodes and links them randomly like big snakes.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.jsdelivr.net/npm/three/build/three.module.js",
          "three/examples/jsm/misc/GPUComputationRenderer.js": "https://cdn.jsdelivr.net/npm/three/examples/jsm/misc/GPUComputationRenderer.js",
          "@jonobr1/force-directed-graph": "https://cdn.jsdelivr.net/npm/@jonobr1/force-directed-graph/build/fdg.module.js"
        }
      }
    </script>
    <script type="module">

      import * as THREE from 'three';
      import { ForceDirectedGraph } from '@jonobr1/force-directed-graph';

      const renderer = new THREE.WebGLRenderer({ antialias: true });
      const scene = new THREE.Scene();
      const camera = new THREE.PerspectiveCamera();

      camera.position.z = 250;

      // Generate some fake data
      const amount = 512;
      const data = {
      nodes: [],  // Required, each element should be an object
      links: []   // Required, each element should be an object
                  // with source and target properties that are
                  // ids of their connecting nodes
      };

      for (let i = 0; i < amount; i++) {

        data.nodes.push({ id: i });
        if (i > 0) {
          data.links.push({ target: Math.floor(Math.random() * i), source: i });
        }

      }

      const fdg = new ForceDirectedGraph(renderer, data);
      scene.add(fdg);

      setup();

      function setup() {
        renderer.setClearColor('#fff');
        document.body.appendChild(renderer.domElement);
        window.addEventListener('resize', resize, false);
        resize();
        renderer.setAnimationLoop(render);
      }

      function resize() {

        const width = window.innerWidth;
        const height = window.innerHeight;

        renderer.setSize(width, height);
        camera.aspect = width / height;
        camera.updateProjectionMatrix();

      }

      function render(elapsed) {
        fdg.update(elapsed);
        renderer.render(scene, camera);
      }

    </script>
  </body>
</html>

[!WARNING] Due to the reliance on the GPU compute rendering, this project is not built for node.js use.

A free and open source tool by Jono Brandel