Skip to content

Repository files navigation

A ten-node network cycling through the force, circle, grid, radial and grouped layouts, recolouring and resizing as it moves

Network Graph Viewer

Note

This project is AI-generated: the code, sample data, and this README were written by Claude Code.

Turn a spreadsheet edge list into an interactive network graph, in your browser or a Jupyter notebook.

Live app: https://adamsm.com/network-graph-viewer/

The sample supervision network as a force-directed graph, nodes coloured by department, with the sidebar for data, filters, style and layout on the left, a statistics panel of network metrics and top nodes on the right, and the edge table below

Features

Getting data in

  • Upload Excel (.xlsx, .xls, .ods), CSV or Parquet files; everything is parsed in memory and never leaves the browser
  • Parquet is read column-typed straight from its own schema, so a zero-padded id column stays text instead of being guessed into a number, and only the byte ranges the file actually needs are read
  • Paste cells straight from Excel or Google Sheets
  • Import JSON and line-delimited JSON (.json, .jsonl, .ndjson): an array of records reads as an edge table, and node-link, the shape d3 and NetworkX write, arrives as nodes and edges at once with the attributes already on the nodes. JSON states its own types, so a zero-padded id stays text here too
  • Import GEXF and GraphML, including node and edge attributes and Gephi's saved positions
  • Import Graphviz DOT (.dot, .gv): defaults, subgraphs and edge chains are flattened onto the tables, clusters arrive as a node column, and a file Graphviz has already laid out keeps its positions
  • Open a #data=… link and the graph it carries is already there; nothing is fetched
  • Load from a public GitHub gist by URL or id, or share a ?gist=… link that opens straight into the graph
  • A workbook with several sheets can use one as the edges and another as node attributes

Shaping the graph

  • Pick which columns are the edge source and target and which show up as edge details
  • Nodes are first-class: they keep their own attributes and can exist without any edges
  • A data table over both the node and edge tables: search, sort, group with count/sum/average/min/max, hide columns, add and delete rows, and edit any cell with the graph updating as you type

Styling

  • Colour nodes by a column or a network metric, size them by degree or any number column, and colour and weight the edges the same way
  • Or let the data do the styling: a column of colours (#b7410e, #b41, rgb(183, 65, 14) or a colour name) paints the nodes and edges exactly as written, and a column of numbers sets radii and stroke widths in pixels, no palette or scale in the way. The shipped Metro lines sample is the case for it, a network whose colours are its identity rather than a category to map
  • Four categorical palettes, including the published colourblind-safe Okabe-Ito and Tol bright sets, and four ramps for numeric rankings
  • Light and dark, switchable in the View menu, and the graph's own colours change with it rather than only the chrome around them
  • Or build your own: edit any slot, add and remove colours, and the palette travels with the workspace, the export and the shared link
  • Node images from a column of https links, data URIs, bare base64, or SVG markup: the picture fills the node and its colour becomes the ring, so an image costs nothing the colours were saying. The shipped Web toolchain sample is 42 projects wearing their logos, and the one place the app fetches anything from a third party. A graph that arrives from a link or a file asks first, since requesting an image tells that server you opened the graph

Reading it without a mouse

Tab into the graph and you land on the most connected node; left and right walk every node in turn, up and down walk the neighbours, Enter selects. Focus is announced, and anything that moves for effect stops when your system asks for less motion.

Filters that chain

Filters apply in order, each one seeing the subgraph the last produced, so two steps out from Alex followed by degree ≥ 2 measures degree inside that neighbourhood. Reordering the same two steps asks a different question.

  • Column values on either the nodes or the edges
  • Degree range, k-core, largest components, ego networks, reciprocated edges only
  • Disparity backbone: keep only the edges carrying more weight than their endpoints' other edges can explain

Measures

Computed on demand and written back as ordinary columns, so every result can immediately drive colour and size, be filtered on, sorted in the table, and travel into an export.

  • Degree, in-degree, out-degree, PageRank, HITS hub and authority, betweenness, closeness, harmonic closeness, eigenvector
  • Louvain modularity classes, with a resolution control; deterministic, so a rerun gives the same communities
  • k-core, triangle counts, connected components
  • Edge measures: shared neighbours, Simmelian strength, disparity-filter significance
  • Whole-graph metrics with plain-language explanations: density, diameter, average path length, clustering

Layouts

Eight layouts that morph into one another rather than jumping:

  • ForceAtlas2 with repulsion, gravity, LinLog and edge-weight controls, Barnes-Hut accelerated
  • Force, hierarchy, radial, circle, grid
  • Circle pack, one disc per group
  • Scripted, positions from your own code
  • Plus an anti-overlap force and a one-shot Noverlap pass

Writing your own

Metrics and layouts you write yourself, run in QuickJS compiled to WebAssembly inside a Web Worker. A script gets a 3 second deadline, a 64 MB ceiling, seeded randomness, and an empty global scope with no network access.

Getting data out

  • SVG and PNG of the current view
  • GEXF including positions and colours, so Gephi opens it looking like it did here
  • GraphML with typed attribute keys
  • Graphviz DOT carrying the colours and sizes on screen, and, for a graph that has been laid out, every node pinned where it sits so neato draws the same picture
  • A .ngv.json workspace holding everything: both tables, the filter chain, styling, layout and node positions
  • CSV of the edge table
  • A link with the whole workspace deflated into its fragment, so sharing the graph is sharing a URL and the data still never reaches a server
  • Save any of it to a GitHub gist with a personal access token, which puts the gist's id in the address bar for a link that stays short whatever the graph weighs

In a Jupyter notebook

The same app runs in a notebook cell, as a widget. Click a node and the kernel sees the selection; edit the table and the edits come back as a DataFrame.

pip install network-graph-viewer

or:

uv add network-graph-viewer
import network_graph_viewer as ngv

w = ngv.show(edges_df, source="from", target="to", color="team")
w                   # the graph, interactive, in the output

w.selected_node     # 'ana'
w.edges             # the edge table, edits and computed columns included

A DataFrame, a list of dicts, a list of (source, target) pairs or a networkx graph all work. The cell shows the graph alone, with a labelled tab on each edge for the panels, and follows the notebook's own light or dark theme. See python/ and the example notebook.

Development

pnpm install
pnpm dev            # start the dev server
pnpm build          # type-check and build to dist/
pnpm build:widget   # rebuild the notebook bundle (generated, not committed)
pnpm lint           # oxlint
pnpm test           # vitest
pnpm format         # oxfmt

The notebook package lives in python/ and is managed with uv:

cd python
uv sync
uv run pytest
uv run pytest --nbmake examples/demo.ipynb

Stack

Releases

Packages

Contributors

Languages