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-purehome.gvRendered 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;
}
- 2,589/2,589Full -Tsvg bytes, macOS
- 2,470/2,589Full -Tsvg bytes, Linux and WASM
- 565/565Layout geometry, main corpus
- 2,008/2,015Layout geometry, 7 engine corpora
- 3,769/3,773Renderers on C’s layouts, 7 formats
- 1,895/1,895C’s own test graphs, 5 variants
- 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:
fsm.gv, C Graphviz's drawingfsm.gv -Kdot76704f31d571…76704f31d571…76704f31d571…76704f31d571…76704f31d571…waiting…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:
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-puretakes the same flags asdotand exits with C's status.- Writes
svg,json,json0,dot,xdot,canon,plainandplain-ext.
import init, { render } from '@graphviz-pure/wasm';
await init();const svg = render('digraph { a -> b [label="fast"] }', 'dot', 'svg');import { initSync, render } from '@graphviz-pure/wasm';import wasm from '@graphviz-pure/wasm/graphviz_pure_wasm_bg.wasm';
initSync({ module: wasm });cargo build --release -p graphviz-pure-clitarget/release/graphviz-pure -Kneato -Tsvg graph.gvWhat “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,cosandatan2are 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.