Skip to content

Graphviz 16.1.0, ported to Rust and WebAssembly

Graphviz, in pure Rust. The same picture on every machine.

The same -Tsvg bytes as C Graphviz on 2,589 of 2,589 test inputs on macOS. Everywhere else one set of bytes on every platform, equal to C on macOS for 2,470 (95.4%).

graphviz_pure cluster_c C Graphviz 16.1.0 cluster_rs graphviz-pure cluster_k -K input->c input->rs c->same rs->K_dot rs->K_neato rs->K_fdp rs->K_sfdp rs->K_twopi rs->K_circo rs->K_osage rs->K_patchwork rs->API rs->notes wasm->same wasm->Playground K_dot->wasm K_neato->wasm K_fdp->wasm K_sfdp->wasm K_twopi->wasm K_circo->wasm K_osage->wasm K_patchwork->wasm same->mac Apple libm same->lin portable libm same->Gallery same->Verify mac->Fidelity lin->Fidelity lin->left lin->xp input input.gv c dot -Tsvg rs graphviz-pure -Tsvg wasm graphviz_pure_wasm, in this tab K_dot dot K_neato neato K_fdp fdp K_sfdp sfdp K_twopi twopi K_circo circo K_osage osage K_patchwork patchwork same same bytes? mac macOS, Apple Silicon 2,589 of 2,589 lin Linux, Windows, Intel Mac, WASM 2,470 of 2,589 Fidelity Fidelity Gallery Gallery Verify Verify it yourself Playground Playground left What's left API API notes Porting notes xp Cross-platform graphviz_pure cluster_c C Graphviz 16.1.0 cluster_rs graphviz-pure cluster_k -K input->c input->rs c->same rs->K_dot rs->K_neato rs->K_fdp rs->K_sfdp rs->K_twopi rs->K_circo rs->K_osage rs->K_patchwork rs->API rs->notes wasm->same wasm->Playground K_dot->wasm K_neato->wasm K_fdp->wasm K_sfdp->wasm K_twopi->wasm K_circo->wasm K_osage->wasm K_patchwork->wasm same->mac Apple libm same->lin portable libm same->Gallery same->Verify mac->Fidelity lin->Fidelity lin->left lin->xp input input.gv c dot -Tsvg rs graphviz-pure -Tsvg wasm graphviz_pure_wasm, in this tab K_dot dot K_neato neato K_fdp fdp K_sfdp sfdp K_twopi twopi K_circo circo K_osage osage K_patchwork patchwork same same bytes? mac macOS, Apple Silicon 2,589 of 2,589 lin Linux, Windows, Intel Mac, WASM 2,470 of 2,589 Fidelity Fidelity Gallery Gallery Verify Verify it yourself Playground Playground left What's left API API notes Porting notes xp Cross-platform graphviz_pure cluster_c C Graphviz 16.1.0 cluster_rs graphviz-pure cluster_k -K input->c input->rs c->same rs->K_dot rs->K_neato rs->K_fdp rs->K_sfdp rs->K_twopi rs->K_circo rs->K_osage rs->K_patchwork rs->API rs->notes wasm->same wasm->Playground K_dot->wasm K_neato->wasm K_fdp->wasm K_sfdp->wasm K_twopi->wasm K_circo->wasm K_osage->wasm K_patchwork->wasm same->mac Apple libm same->lin portable libm same->Gallery same->Verify mac->Fidelity lin->Fidelity lin->left lin->xp input input.gv c dot -Tsvg rs graphviz-pure -Tsvg wasm graphviz_pure_wasm, in this tab K_dot dot K_neato neato K_fdp fdp K_sfdp sfdp K_twopi twopi K_circo circo K_osage osage K_patchwork patchwork same same bytes? mac macOS, Apple Silicon 2,589 of 2,589 lin Linux, Windows, Intel Mac, WASM 2,470 of 2,589 Fidelity Fidelity Gallery Gallery Verify Verify it yourself Playground Playground left What's left API API notes Porting notes xp Cross-platform graphviz_pure cluster_c C Graphviz 16.1.0 cluster_rs graphviz-pure cluster_k -K input->c input->rs c->same rs->K_dot rs->K_neato rs->K_fdp rs->K_sfdp rs->K_twopi rs->K_circo rs->K_osage rs->K_patchwork rs->API rs->notes wasm->same wasm->Playground K_dot->wasm K_neato->wasm K_fdp->wasm K_sfdp->wasm K_twopi->wasm K_circo->wasm K_osage->wasm K_patchwork->wasm same->mac Apple libm same->lin portable libm same->Gallery same->Verify mac->Fidelity lin->Fidelity lin->left lin->xp input input.gv c dot -Tsvg rs graphviz-pure -Tsvg wasm graphviz_pure_wasm, in this tab K_dot dot K_neato neato K_fdp fdp K_sfdp sfdp K_twopi twopi K_circo circo K_osage osage K_patchwork patchwork same same bytes? mac macOS, Apple Silicon 2,589 of 2,589 lin Linux, Windows, Intel Mac, WASM 2,470 of 2,589 Fidelity Fidelity Gallery Gallery Verify Verify it yourself Playground Playground left What's left API API notes Porting notes xp Cross-platform

Rendered by graphviz-pure when this site was built.

DOT source of this frame
digraph graphviz_pure {
  graph [rankdir=LR, bgcolor="white", fontname="Times-Roman", fontsize=14, fontcolor="black", pad="0.2",
         nodesep=0.22, ranksep=0.42, overlap=false, splines=true, outputorder=edgesfirst]
  node [fontname="Times-Roman", fontsize=14, height=0.4, color="black", fontcolor="black"]
  edge [len=2.2, fontname="Times-Roman", fontsize=11, arrowsize=0.75, color="black", fontcolor="black"]

  "input" [shape=note, label="input.gv"]
  subgraph cluster_c {
    label="C Graphviz 16.1.0"; style=filled; color="lightgrey";
    node [style=filled, fillcolor="white", color="white"];
    "c" [label="dot -Tsvg"]
  }
  subgraph cluster_rs {
    label="graphviz-pure"; color="blue";
    node [style=filled, fillcolor="lightblue2"];
    "rs" [label="graphviz-pure -Tsvg"]
    "wasm" [label="graphviz_pure_wasm, in this tab"]
    subgraph cluster_k {
      label="-K"; style=dashed; color="gray50"; fontsize=11;
      node [fontsize=12, height=0.3, width=0.6, margin="0.05,0.02"];
      "K_dot" [style=filled, fillcolor=lightgoldenrod1, fontcolor=black, label="dot"]; "K_neato" [style=solid, label="neato"]; "K_fdp" [style=solid, label="fdp"]; "K_sfdp" [style=solid, label="sfdp"]; "K_twopi" [style=solid, label="twopi"]; "K_circo" [style=solid, label="circo"]; "K_osage" [style=solid, label="osage"]; "K_patchwork" [style=solid, label="patchwork"]
    }
  }
  "same" [shape=diamond, style=filled, fillcolor="lightyellow", label="same\nbytes?"]
  "mac" [shape=record, label="{macOS, Apple Silicon|2,589 of 2,589}"]
  "lin" [shape=record, label="{Linux, Windows, Intel Mac, WASM|2,470 of 2,589}"]

  input -> c; input -> rs;
  rs -> {K_dot K_neato K_fdp K_sfdp K_twopi K_circo K_osage K_patchwork} [arrowhead=none, color="gray60"];
  {K_dot K_neato K_fdp K_sfdp K_twopi K_circo K_osage K_patchwork} -> wasm [arrowhead=none, color="gray60"];
  c -> same; wasm -> same;
  same -> mac [label="Apple libm"];
  same -> lin [label="portable libm"];

  node [shape=box, style=rounded, fontsize=13];
  "Fidelity" [URL="/fidelity/", tooltip="Fidelity", label="Fidelity"]
  "Gallery" [URL="/gallery/", tooltip="Gallery", label="Gallery"]
  "Verify" [URL="/verify/", tooltip="Verify it yourself", label="Verify it yourself"]
  "Playground" [URL="/playground/", tooltip="Playground", label="Playground"]
  "left" [URL="/whats-left/", tooltip="What's left", label="What's left"]
  "API" [URL="/api/", tooltip="API", label="API"]
  "notes" [URL="/porting/dotgen/", tooltip="Porting notes", label="Porting notes"]
  "xp" [URL="/cross-platform/", tooltip="Cross-platform", label="Cross-platform"]
  lin -> xp;
  mac -> Fidelity; lin -> Fidelity; lin -> left; same -> Gallery; same -> Verify;
  wasm -> Playground; rs -> API; rs -> notes;

}
  1. 2,589/2,589Full -Tsvg bytes, macOS
  2. 2,470/2,589Full -Tsvg bytes, Linux and WASM
  3. 565/565Layout geometry, main corpus
  4. 2,008/2,015Layout geometry, 7 engine corpora
  5. 3,769/3,773Renderers on C’s layouts, 7 formats
  6. 1,895/1,895C’s own test graphs, 5 variants
  7. 98.8%Random graphs, fuzz round 4 (6,000 cases)

Same picture everywhere

One input, one set of bytes, on every machine.

Off macOS the port does its floating-point math with functions compiled into it, so its output does not depend on the operating system, the CPU or the browser. CI renders all 2,591 corpus inputs and 30 gallery samples on Linux x86-64, Linux arm64, Windows x86-64, macOS arm64, WebAssembly (Node.js), and fails unless every file comes out the same. Your browser is one more machine:

Finite-state machine, drawn by Graphviz
fsm.gv, C Graphviz's drawing
fsm.gv -Kdot
Linux x86-64native76704f31d571…
Linux arm64native76704f31d571…
Windows x86-64native76704f31d571…
macOS arm64portable math76704f31d571…
WebAssemblyNode.js 2276704f31d571…
This tabyour browser, WASMwaiting…

Your browser renders this when the section scrolls into view.

C Graphviz itself is not the same everywhere. Compiled on Linux, it takes that platform's math library, qsort and rand, and drifts from C on macOS:

  • C Graphviz on Linuxbuilt the way Homebrew builds it on macOS2,005/2,586 · 77.5%
  • graphviz-pure, portable buildLinux, Windows, Intel Macs, WASM, your browser2,470/2,589 · 95.4%
  • graphviz-pure, portable buildagainst C running the same math functions2,590/2,590 · 100%

Inputs whose -Tsvg equals C Graphviz 16.1.0 on macOS, the first two; the third against C linked with the port's portable math. Every platform's files, side by side · the CI run

What it is

A function-by-function port of the Graphviz 16.1.0 C sources: all eight layout engines (dot, neato,fdp, sfdp, twopi, circo, osage, patchwork), and the libraries under them: network simplex, pathplan, ortho, pack, vpsc, the shape, record and HTML-table code, and the output plugins.

  • #![forbid(unsafe_code)]; no C, no FFI, no system Graphviz.
  • Runs natively and in the browser (wasm32-unknown-unknown, 1.7 MB, about 700 KB compressed).
  • graphviz-pure takes the same flags as dot and exits with C's status.
  • Writes svg, json, json0, dot, xdot, canon, plain and plain-ext.
Browser, Node.js, Deno or Bun
import init, { render } from '@graphviz-pure/wasm';
await init();
const svg = render('digraph { a -> b [label="fast"] }', 'dot', 'svg');
Cloudflare Workers
import { initSync, render } from '@graphviz-pure/wasm';
import wasm from '@graphviz-pure/wasm/graphviz_pure_wasm_bg.wasm';
initSync({ module: wasm });
Command line
cargo build --release -p graphviz-pure-cli
target/release/graphviz-pure -Kneato -Tsvg graph.gv

What “identical” means here

Equal bytes. The two outputs are compared after dropping one line, the <!-- Generated by … -->comment that names the program. Nothing else is normalised: not whitespace, not number formatting, not element order.

Against which C. The reference is Graphviz 16.1.0 with only its core, dot and neato plugins loaded, so no Pango plugin is available and C measures text with its built-in font metric tables, as the port does. A stock dot that measures text with Pango and cairo will place labels differently, and so will not match either build. The setup

Which machine. C on macOS calls Apple's libm. The port reproduces it bit for bit on macOS, and elsewhere uses a portable math library compiled into it, the same on every platform, which is why Linux, Windows and WASM match on 2,470 files rather than 2,589. Math off macOS

What is not identical yet

  • 119 files under portable math: sfdp 114, fdp 3, main 1, circo 1. Apple's pow, sin, cos andatan2 are closed source, and sfdp's multilevel layout magnifies their last-bit differences.
  • 7 twopi layouts out of 289: a floating-point tie in edge routing that C itself breaks differently from run to run. 12 corpus files are skipped because C's own output changes between two runs.
  • 1.2% of 6,000 random graphs in the last fuzzing round: mostly C nondeterminism and C crash paths, plus open differences.
  • 14 open issues: 3 in the port, 11 upstream C behaviour. What's left

Check it

  • FidelityEvery check, per corpus and per math profile, with the files that differ.
  • Gallery30 inputs across all eight engines, C next to Rust, with overprint, blink, pixel and source diffs. 29 of them are re-rendered in your browser and compared with C's bytes.
  • PlaygroundYour own DOT, through the WASM build.
  • Verify it yourselfThe commands, the C reference setup, and where the goldens live.
  • Porting notesEach Rust module mapped to its C file, and every known difference.
  • APIrustdoc for the workspace crates.