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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@graphty/layout

v1.2.9

Published

graph layout algorithms based on networkx

Readme

Layout

CI Coverage Status npm version License: MIT Examples

View Interactive Examples →

Layout is a TypeScript library for positioning nodes in graphs. It's a TypeScript port of the layout algorithms from the Python NetworkX library.

Features

The library offers various graph layout algorithms, including:

  • Random Layout - Places nodes randomly in a unit square
  • Circular Layout - Places nodes on a circle
  • Shell Layout - Places nodes in concentric circles (shells)
  • Spring Layout (Fruchterman-Reingold) - Force-directed layout with attractions and repulsions
  • Spectral Layout - Uses eigenvectors of the graph's Laplacian matrix
  • Spiral Layout - Places nodes along a spiral
  • Bipartite Layout - Layout for bipartite graphs in two straight lines
  • Multipartite Layout - Layout for multipartite graphs in levels
  • BFS Layout - Layout based on breadth-first search algorithm
  • Planar Layout - Planar layout without edge crossings
  • Kamada-Kawai Layout - Layout based on path-length cost functions
  • ForceAtlas2 Layout - Advanced force-directed algorithm
  • ARF Layout - Layout with attractive and repulsive forces

Additionally, the library includes:

Graph Generators for creating common graph types:

  • Complete Graph - All nodes connected to each other
  • Cycle Graph - Nodes connected in a circular path
  • Star Graph - Central hub connected to all other nodes
  • Wheel Graph - Hub connected to nodes arranged in a rim cycle
  • Grid Graph - 2D grid with nodes connected to neighbors
  • Random Graph - Erdős–Rényi random graph model
  • Bipartite Graph - Graph with two disjoint node sets
  • Scale-Free Graph - Barabási–Albert preferential attachment model

Layout Helpers for intelligent graph analysis and layout optimization:

  • groupNodes - Universal node grouping by degree, distance, k-core, or community
  • detectBipartite - Automatic bipartite graph detection
  • findBestRoot - Optimal root selection for tree layouts
  • autoConfigureForce - Smart parameter configuration for force layouts
  • layoutQuality - Layout quality measurement
  • combineLayouts - Blend multiple layout algorithms
  • interpolateLayouts - Smooth animation between layouts

Installation

npm install @graphty/layout

How to Use

Import the library in your TypeScript/JavaScript project:

import {
  // Layout algorithms
  randomLayout,
  circularLayout,
  springLayout,
  fruchtermanReingoldLayout,
  spectralLayout,
  spiralLayout,
  bipartiteLayout,
  multipartiteLayout,
  bfsLayout,
  planarLayout,
  kamadaKawaiLayout,
  forceatlas2Layout,
  arfLayout,
  rescaleLayout,

  // Graph generators
  completeGraph,
  cycleGraph,
  starGraph,
  wheelGraph,
  gridGraph,
  randomGraph,
  bipartiteGraph,
  scaleFreeGraph,

  // Layout helpers
  groupNodes,
  detectBipartite,
  findBestRoot,
  autoConfigureForce,
  layoutQuality,
  combineLayouts,
  interpolateLayouts
} from '@graphty/layout'

Quick Start

// Generate a graph
const graph = scaleFreeGraph(30, 2, 42)

// Auto-configure and layout
const config = autoConfigureForce(graph)
const positions = springLayout(graph, config.k, null, null, config.iterations)

// Or use specialized layouts
const bipartite = detectBipartite(graph)
if (bipartite) {
  const positions = bipartiteLayout(graph, bipartite.setA)
}

// Or use shell layout with automatic grouping
const shells = groupNodes(graph, 'degree', 3)
const positions = shellLayout(graph, shells)

Graph Structure

The module accepts graphs in two formats:

1. Graph Object with methods (preferred)

const graph = {
  nodes: () => [0, 1, 2, 3],
  edges: () => [
    [0, 1],
    [1, 2],
    [2, 3],
    [3, 0]
  ],
  getEdgeData: (source, target, attr) => number // optional for edge weights
}

2. Simple array of nodes

const nodes = [0, 1, 2, 3]

Graph Generation

The library includes utilities to generate common graph types for testing and demonstration:

Complete Graph

Creates a complete graph with all possible edges between nodes.

const graph = completeGraph(5)
// Creates a graph with 5 nodes (0-4) and 10 edges (all pairs connected)

Cycle Graph

Creates a cycle graph where nodes form a closed loop.

const graph = cycleGraph(6)
// Creates a graph with 6 nodes (0-5) connected in a cycle: 0-1-2-3-4-5-0

Star Graph

Creates a star graph with one central hub connected to all other nodes.

const graph = starGraph(7)
// Creates a graph with 7 nodes where node 0 is connected to all others (1-6)

Wheel Graph

Creates a wheel graph - a hub connected to all nodes of a rim cycle.

const graph = wheelGraph(6)
// Creates a graph with 6 nodes: hub (0) connected to rim cycle (1-2-3-4-5-1)

Grid Graph

Creates a 2D grid graph with specified rows and columns.

const graph = gridGraph(3, 4)
// Creates a 3x4 grid with nodes named "row,col" (e.g., "0,0", "0,1", etc.)
// Nodes are connected to their horizontal and vertical neighbors

Random Graph

Creates a random graph with specified edge probability.

const graph = randomGraph(10, 0.3, 42)
// Creates a graph with 10 nodes (0-9)
// Each possible edge has 30% chance of existing
// Seed 42 ensures reproducible results

Bipartite Graph

Creates a bipartite graph with two sets of nodes.

const graph = bipartiteGraph(3, 4, 0.5, 123)
// Creates two sets: A0,A1,A2 and B0,B1,B2,B3
// Each edge between sets has 50% chance of existing
// Returns graph with additional setA and setB properties

Scale-Free Graph

Creates a scale-free graph using the Barabási-Albert preferential attachment model.

const graph = scaleFreeGraph(20, 2, 456)
// Creates a graph with 20 nodes
// Each new node connects to 2 existing nodes (preferential attachment)
// Results in a power-law degree distribution with some high-degree hubs

Using Generated Graphs with Layouts

All generated graphs work seamlessly with the layout algorithms:

// Generate a complete graph and apply circular layout
const graph = completeGraph(8)
const positions = circularLayout(graph)

// Generate a grid with auto-configured spring layout
const grid = gridGraph(5, 5)
const config = autoConfigureForce(grid)
const gridPositions = springLayout(
  grid,
  config.k,
  null,
  null,
  config.iterations
)

// Generate a scale-free network with optimized ForceAtlas2
const network = scaleFreeGraph(50, 3, 42)
const networkConfig = autoConfigureForce(network)
const networkPositions = forceatlas2Layout(
  network,
  null,
  networkConfig.iterations,
  1.0,
  networkConfig.scalingRatio,
  networkConfig.gravity
)

// Use bipartite graph with automatic detection
const bipartite = bipartiteGraph(5, 7, 0.4, 123)
const bipartitePositions = bipartiteLayout(bipartite, bipartite.setA)

Layout Helpers

The library includes helper functions to simplify working with complex layouts:

groupNodes() - Universal Node Grouping

Groups nodes for shell, multipartite, or custom layouts based on various metrics:

// Group by degree (connectivity) - great for shell layouts
const shells = groupNodes(graph, 'degree', 3)
const positions = shellLayout(graph, shells)

// Group by distance from root - perfect for hierarchical layouts
const layers = groupNodes(graph, 'bfs', 0, { root: 'A' })
const positions = multipartiteLayout(graph, layers)

// Group by k-core (dense subgraphs) - ideal for social networks
const cores = groupNodes(graph, 'k-core')
const positions = shellLayout(graph, cores)

// Group by community detection - useful for modular networks
const communities = groupNodes(graph, 'community', 5)

detectBipartite() - Automatic Bipartite Detection

Automatically detects if a graph is bipartite and finds the two sets:

const result = detectBipartite(graph)
if (result) {
  // Graph is bipartite! Use specialized layout
  const positions = bipartiteLayout(graph, result.setA)
} else {
  // Not bipartite, use general layout
  const positions = springLayout(graph)
}

findBestRoot() - Optimal Root Node Selection

Finds the best starting node for tree-like layouts (BFS, hierarchical):

const root = findBestRoot(graph)
const positions = bfsLayout(graph, root)

autoConfigureForce() - Smart Force Layout Configuration

Automatically configures parameters based on graph properties:

const config = autoConfigureForce(graph)

// Use with Fruchterman-Reingold
const positions = springLayout(graph, config.k, null, null, config.iterations)

// Use with ForceAtlas2
const positions = forceatlas2Layout(
  graph,
  null,
  config.iterations,
  1.0,
  config.scalingRatio,
  config.gravity
)

layoutQuality() - Layout Quality Metrics

Measure and compare layout quality:

const circular = circularLayout(graph)
const spring = springLayout(graph)

const metricsC = layoutQuality(graph, circular)
const metricsS = layoutQuality(graph, spring)

console.log('Circular layout - avg edge length:', metricsC.avgEdgeLength)
console.log('Spring layout - avg edge length:', metricsS.avgEdgeLength)
console.log('Spring layout - min node distance:', metricsS.minNodeDistance)

combineLayouts() - Blend Multiple Layouts

Create hybrid layouts by combining different algorithms:

const circular = circularLayout(graph)
const spring = springLayout(graph)

// 30% circular structure, 70% force-directed
const hybrid = combineLayouts([circular, spring], [0.3, 0.7])

interpolateLayouts() - Smooth Layout Transitions

Create animation frames between different layouts:

const startLayout = circularLayout(graph)
const endLayout = springLayout(graph)

// Generate 30 frames for smooth animation
const frames = interpolateLayouts(startLayout, endLayout, 30)
// Use frames[0] through frames[30] for animation

Helper Usage Patterns

Smart Shell Layout

// Automatically choose best grouping method based on graph density
const n = graph.nodes().length
const m = graph.edges().length
const density = (2 * m) / (n * (n - 1))

const method = density < 0.1 ? 'bfs' : density > 0.5 ? 'k-core' : 'degree'
const shells = groupNodes(graph, method)
const positions = shellLayout(graph, shells)

Adaptive Layout Selection

// Choose layout based on graph properties
let positions

if (detectBipartite(graph)) {
  const { setA } = detectBipartite(graph)
  positions = bipartiteLayout(graph, setA)
} else if (graph.nodes().length > 100) {
  // Large graph - use fast layout
  positions = circularLayout(graph)
} else {
  // Default to auto-configured force layout
  const config = autoConfigureForce(graph)
  positions = springLayout(graph, config.k, null, null, config.iterations)
}

Progressive Layout Refinement

// Start with fast layout, progressively refine
const initial = circularLayout(graph)
const refined = springLayout(graph, null, initial, null, 50)
const final = kamadaKawaiLayout(graph, null, refined)

Layout Helper Quick Reference

| Helper Function | Purpose | Best Use Case | | -------------------------------- | ------------------------------- | ------------------------------------- | | groupNodes(graph, 'degree') | Group by connectivity | Shell layouts for scale-free networks | | groupNodes(graph, 'bfs') | Group by distance from root | Hierarchical/tree layouts | | groupNodes(graph, 'k-core') | Group by subgraph density | Social network analysis | | groupNodes(graph, 'community') | Group by detected communities | Modular network visualization | | detectBipartite(graph) | Check if graph is bipartite | Matching problems, assignments | | findBestRoot(graph) | Find optimal tree root | BFS layout, hierarchical layout | | autoConfigureForce(graph) | Auto-configure force parameters | Any force-directed layout | | layoutQuality(graph, pos) | Measure layout quality | Comparing different layouts | | combineLayouts([...], [...]) | Blend multiple layouts | Custom hybrid visualizations | | interpolateLayouts(from, to) | Create animation frames | Interactive transitions |

Usage Examples

Circular Layout

// Use our graph generator instead of manual construction
const graph = cycleGraph(8)

const positions = circularLayout(graph)
// Nodes arranged in a perfect circle

Spring Layout (Fruchterman-Reingold)

// Generate a grid and apply force-directed layout with auto-configured parameters
const graph = gridGraph(5, 5)
const config = autoConfigureForce(graph)

const positions = springLayout(
  graph,
  config.k, // optimal distance
  null, // initial positions
  null, // fixed nodes
  config.iterations // iterations
)

// Or use fruchtermanReingoldLayout (same function)
const positions2 = fruchtermanReingoldLayout(graph, config.k)

Bipartite graph layout

// Generate a bipartite graph and detect sets automatically
const graph = bipartiteGraph(4, 6, 0.5, 42)

// Option 1: Use the built-in sets
const positions = bipartiteLayout(graph, graph.setA, 'vertical')

// Option 2: Auto-detect bipartite structure
const detected = detectBipartite(graph)
if (detected) {
  const positions2 = bipartiteLayout(graph, detected.setA, 'horizontal')
}

3D Support

All layout algorithms support 3D positioning by setting the dim parameter to 3:

// Any layout algorithm in 3D
const positions3D = springLayout(graph, null, null, null, 50, 1, [0, 0, 0], 3)
// Returns: { node1: [x, y, z], node2: [x, y, z], ... }

// ForceAtlas2 in 3D
const fa3D = forceatlas2Layout(graph, null, 100, 1, 2, 1, false, false, null, null, null, false, false, 42, 3)

// Circular layout in 3D (creates a sphere)
const circular3D = circularLayout(graph, 1, [0, 0, 0], 3)

When using 3D layouts, positions will have three coordinates [x, y, z] instead of two.

Common Parameters

Most layout functions share these parameters:

  • scale (number): Scale factor for positions (default: 1)
  • center (number[]): Center coordinates around which to center the layout (default: [0, 0] for 2D, [0, 0, 0] for 3D)
  • dim (number): Layout dimension - 2D or 3D (default: 2)
  • seed (number): Seed for random generation (for reproducible layouts)

TypeScript Types

type Node = string | number
type Edge = [Node, Node]
type PositionMap = Record<Node, number[]>

interface Graph {
  nodes?: () => Node[]
  edges?: () => Edge[]
  getEdgeData?: (source: Node, target: Node, attr: string) => any
}

Utilities

Layout Rescaling

import { rescaleLayout } from './layout.js'

// Rescale existing positions
const scaledPositions = rescaleLayout(positions, 2.0, [10, 10])

Available Algorithms

Force-Directed Layouts

  • springLayout() / fruchtermanReingoldLayout() - Classic force-directed algorithm
  • forceatlas2Layout() - Advanced algorithm with many configuration options
  • arfLayout() - Attractive and repulsive forces
  • kamadaKawaiLayout() - Based on shortest-path distances

Geometric Layouts

  • randomLayout() - Random placement
  • circularLayout() - Circular arrangement
  • shellLayout() - Concentric circles
  • spiralLayout() - Spiral arrangement

Specialized Layouts

  • spectralLayout() - Based on eigenvectors of the Laplacian matrix
  • bipartiteLayout() - For bipartite graphs
  • multipartiteLayout() - For multi-level graphs
  • bfsLayout() - Based on breadth-first search
  • planarLayout() - For planar graphs without crossings

Utilities

  • rescaleLayout() - Rescale and recenter positions
  • rescaleLayoutDict() - Rescale a dictionary of positions

Detailed Examples for each Algorithm

Random Layout

// Generate any graph and apply random layout
const graph = completeGraph(10)
const positions = randomLayout(graph, [0, 0], 2, 42)
// Nodes randomly placed in unit square with seed 42

Circular Layout

// Perfect for cyclic or complete graphs
const graph = cycleGraph(12)
const positions = circularLayout(graph)
// 12 nodes evenly spaced on a circle

Shell Layout

// Use automatic node grouping for shell layout
const graph = scaleFreeGraph(30, 2, 42)

// Group nodes by degree (hubs in center)
const shells = groupNodes(graph, 'degree', 3)
const positions = shellLayout(graph, shells)

// Or group by k-core for social networks
const kCoreShells = groupNodes(graph, 'k-core')
const positions2 = shellLayout(graph, kCoreShells)

Spring Layout (Fruchterman-Reingold)

// Auto-configure parameters based on graph size
const graph = randomGraph(20, 0.2, 42)
const config = autoConfigureForce(graph)

const positions = springLayout(
  graph,
  config.k, // optimal distance
  null, // initial positions
  null, // fixed nodes
  config.iterations // iterations
)

Spectral Layout

// Great for revealing graph structure
const graph = gridGraph(6, 6)
const positions = spectralLayout(graph)
// Grid structure preserved in spectral embedding

Spiral Layout

// Perfect for sequential or time-based data
const graph = cycleGraph(50)
const positions = spiralLayout(
  graph,
  1, // scale
  [0, 0], // center
  2, // dim
  0.35, // resolution
  true // equidistant points
)

Bipartite Layout

// Generate bipartite graph and layout automatically
const graph = bipartiteGraph(5, 7, 0.4, 42)
const positions = bipartiteLayout(
  graph,
  graph.setA, // first group nodes (auto-generated)
  'vertical', // align: 'vertical' or 'horizontal'
  1, // scale
  [0, 0], // center
  4 / 3 // aspectRatio
)

Multipartite Layout

// Use automatic layer detection with groupNodes
const graph = scaleFreeGraph(20, 2, 42)
const layers = groupNodes(graph, 'bfs', 0, { root: findBestRoot(graph) })

// Convert to multipartite format
const layerMap = {}
layers.forEach((nodes, i) => {
  layerMap[i] = nodes
})

const positions = multipartiteLayout(
  graph,
  layerMap, // subsetKey: layer mapping
  'vertical', // align
  1, // scale
  [0, 0] // center
)

BFS Layout

// Use automatic root detection for tree-like graphs
const graph = starGraph(10)
const root = findBestRoot(graph) // Automatically finds node 0 (hub)

const positions = bfsLayout(
  graph,
  root, // start: best root node
  'vertical', // align
  1, // scale
  [0, 0] // center
)

Planar Layout

// Create a planar graph (grid is always planar)
const graph = gridGraph(4, 4)
const positions = planarLayout(graph, 1, [0, 0], 2)
// Note: throws error if graph is not planar

// For unknown graphs, check planarity first
if (isPlanar(graph)) {
  const positions = planarLayout(graph)
} else {
  // Fall back to non-planar layout
  const positions = springLayout(graph)
}

Kamada-Kawai Layout

// Great for small to medium graphs
const graph = wheelGraph(8)
const positions = kamadaKawaiLayout(
  graph,
  null, // dist: distance matrix (auto)
  null, // pos: initial positions (auto)
  'weight', // weight: edge weight attribute
  1, // scale
  [0, 0], // center
  2 // dim
)

ForceAtlas2 Layout

// Auto-configure for your graph type
const graph = scaleFreeGraph(50, 3, 42)
const config = autoConfigureForce(graph)

const positions = forceatlas2Layout(
  graph,
  null, // pos: initial positions
  config.iterations, // maxIter: auto-configured
  1.0, // jitterTolerance
  config.scalingRatio, // scalingRatio: auto-configured
  config.gravity, // gravity: auto-configured
  false, // distributedAction
  false, // strongGravity
  null, // nodeMass: node masses
  null, // nodeSize: node sizes
  null, // weight: weight attribute
  true, // dissuadeHubs: good for scale-free
  false, // linlog: logarithmic attraction
  42, // seed
  2 // dim: 2 for 2D, 3 for 3D
)

// 3D layout example
const positions3D = forceatlas2Layout(
  graph,
  null,
  100,
  1.0,
  2.0,
  1.0,
  false,
  false,
  null,
  null,
  null,
  false,
  false,
  42,
  3 // 3D mode - returns [x, y, z] coordinates
)

ARF Layout

// Layout with attractive and repulsive forces
const graph = completeGraph(10)
const positions = arfLayout(
  graph,
  null, // pos: initial positions
  1, // scaling
  1.1, // a: spring force (must be > 1)
  1000, // maxIter
  42 // seed
)

Advanced Examples: Combining Generators and Helpers

Example 1: Community-Based Visualization

// Generate a scale-free network (hubs and communities)
const graph = scaleFreeGraph(100, 3, 42)

// Detect communities and use them for shell layout
const communities = groupNodes(graph, 'community', 4)
const positions = shellLayout(graph, communities)

// Or use communities for coloring in your visualization
const communityMap = new Map()
communities.forEach((nodes, idx) => {
  nodes.forEach(node => communityMap.set(node, idx))
})

Example 2: Adaptive Layout Selection

function chooseOptimalLayout(graph) {
  const n = graph.nodes().length
  const m = graph.edges().length
  const density = (2 * m) / (n * (n - 1))

  // Check for special graph types
  const bipartite = detectBipartite(graph)
  if (bipartite) {
    return bipartiteLayout(graph, bipartite.setA)
  }

  // Choose based on graph properties
  if (n > 100) {
    // Large graph - use fast circular layout
    return circularLayout(graph)
  } else if (density < 0.1) {
    // Sparse graph - use spring layout
    const config = autoConfigureForce(graph)
    return springLayout(graph, config.k, null, null, config.iterations)
  } else {
    // Dense graph - use spectral or kamada-kawai
    return n < 50 ? kamadaKawaiLayout(graph) : spectralLayout(graph)
  }
}

// Usage
const graph = randomGraph(30, 0.3, 42)
const positions = chooseOptimalLayout(graph)

Example 3: Animated Layout Transitions

// Start with circular layout
const graph = completeGraph(15)
const startLayout = circularLayout(graph)

// Optimize with force-directed
const config = autoConfigureForce(graph)
const endLayout = springLayout(graph, config.k, null, null, config.iterations)

// Create smooth animation frames
const frames = interpolateLayouts(startLayout, endLayout, 60)

// Use frames[0] through frames[60] for animation
function animate(frameIndex) {
  const positions = frames[frameIndex]
  // Update your visualization with these positions
}

Example 4: Hierarchical Network Analysis

// Generate a preferential attachment network
const graph = scaleFreeGraph(50, 2, 42)

// Find natural hierarchy using k-core decomposition
const kCores = groupNodes(graph, 'k-core')

// Layout with most connected nodes in center
const positions = shellLayout(graph, kCores)

// Or create a tree-like view
const root = findBestRoot(graph)
const bfsPositions = bfsLayout(graph, root)

Example 5: Quality-Driven Layout

// Try multiple layouts and pick the best
function findBestLayout(graph) {
  const candidates = [
    { name: 'circular', positions: circularLayout(graph) },
    { name: 'spectral', positions: spectralLayout(graph) },
    { name: 'spring', positions: springLayout(graph) }
  ]

  let best = candidates[0]
  let bestScore = Infinity

  candidates.forEach(candidate => {
    const quality = layoutQuality(graph, candidate.positions)
    const score = quality.edgeLengthStdDev / quality.avgEdgeLength

    if (score < bestScore) {
      bestScore = score
      best = candidate
    }
  })

  console.log(`Best layout: ${best.name} (score: ${bestScore.toFixed(3)})`)
  return best.positions
}

Example 6: Hybrid Layouts

// Create a graph with clear structure
const graph = gridGraph(6, 6)

// Get geometric and force-based layouts
const grid = circularLayout(graph)
const force = springLayout(graph)

// Blend them: 40% geometric structure, 60% force optimization
const hybrid = combineLayouts([grid, force], [0.4, 0.6])

// The result preserves some grid structure while optimizing edge lengths

Error Handling

try {
  const positions = planarLayout(nonPlanarGraph)
} catch (error) {
  console.error('Graph is not planar:', error.message)
}

try {
  const positions = arfLayout(graph, null, 1, 0.5) // a <= 1
} catch (error) {
  console.error('Invalid parameter a:', error.message)
}

Installation

npm install @graphty/layout

Building from Source

If you want to build the TypeScript module from source:

  1. Clone the repository:

    git clone https://github.com/graphty-org/layout.git
    cd layout
  2. Install dependencies:

    npm install
  3. Compile TypeScript to JavaScript:

    npm run build

    This will compile the layout.ts file to JavaScript and generate type declarations in the dist/ directory.

  4. For development with automatic compilation:

    npm run dev

    This will watch for changes and automatically recompile the TypeScript files.

Development Server

The project includes a Vite development server for testing and viewing examples.

Starting the Development Server

npm run serve

This will:

  • Start a development server on port 3000
  • Automatically open your browser to /examples/
  • Provide hot module reloading for development

Configuring the Development Server

You can customize the server configuration using environment variables:

  1. Create a .env file (copy from .env.example):

    cp .env.example .env
  2. Configure server options in .env:

    # Server host (defaults to true for network exposure)
    HOST=localhost    # For local-only access
    HOST=0.0.0.0     # For network access
    HOST=my.server.com # For custom domain
    
    # Server port (defaults to 3000)
    PORT=3000
    PORT=8080        # Custom port
  3. Start the server with your configuration:

    npm run serve

Alternative: Build and Serve

To build the project and then serve the examples:

npm run examples

This command:

  1. Builds the TypeScript files to JavaScript
  2. Starts the Vite development server

Note: The compiled JavaScript files will be available in the dist/ directory. You can import from the compiled JavaScript files or directly use the TypeScript source files in a TypeScript project.

Implementation

The module includes complete implementations of:

  • Random Number Generator with seed support for reproducible results
  • Mathematical utilities similar to NumPy for multidimensional array operations
  • Force-directed algorithms with L-BFGS optimization for Kamada-Kawai
  • Planarity algorithms including Left-Right test for planar graphs
  • Auto-scaling system to automatically normalize positions

Performance Tips

  1. Large Graphs (>1000 nodes):

    // Use fast layouts first
    const initial = circularLayout(graph)
    // Then refine with limited iterations
    const refined = springLayout(graph, null, initial, null, 50)
  2. Dense Graphs:

    // Use spectral layout for dense graphs
    const density = (2 * m) / (n * (n - 1))
    if (density > 0.5) {
      const positions = spectralLayout(graph)
    }
  3. Real-time Updates:

    // Pre-calculate layout quality
    const quality = layoutQuality(graph, positions)
    // Use interpolation for smooth updates
    const frames = interpolateLayouts(oldPositions, newPositions, 30)
  4. Memory Optimization:

    // For very large graphs, use generators
    function* layoutInChunks(graph, chunkSize = 100) {
      const nodes = graph.nodes()
      for (let i = 0; i < nodes.length; i += chunkSize) {
        const chunk = nodes.slice(i, i + chunkSize)
        // Process chunk...
        yield chunk
      }
    }

Development

Building the Project

The project uses a unified build system:

# Build TypeScript to JavaScript
npm run build

# Build the bundled ES module (dist/layout.js)
npm run build:bundle

# Build everything
npm run build:all

Running Examples Locally

# Start development server with examples
npm run examples
# or
npm run serve

This will:

  1. Build the bundled dist/layout.js
  2. Start a Vite dev server at http://localhost:3000
  3. Automatically redirect imports to use the bundled version

Building for GitHub Pages

To build a static site for GitHub Pages deployment:

# Build static site in gh-pages/ directory
npm run build:gh-pages

This creates a gh-pages/ directory with:

  • All example HTML files
  • The bundled layout.js
  • All necessary assets

To deploy to GitHub Pages, see gh-pages/DEPLOY.md after building.

Development Workflow

  1. Make changes to TypeScript source files in src/
  2. Test locally with npm run examples
  3. Run tests with npm test
  4. Build for production with npm run build:all
  5. Deploy examples with npm run build:gh-pages

Recent Updates

Version 1.2.7 (Latest)

  • Fixed: ForceAtlas2 now correctly returns numeric Z coordinates in 3D mode (previously returned NaN)
  • Fixed: NPM package now uses the correct bundled entry point (dist/layout.js)
  • Added: Full 3D support documentation and examples

Contributing

This project is a TypeScript port of the NetworkX Python library. For contributions and issues, visit the GitHub repository.

License

MIT License - see LICENSE file for details.