Vizdom is a declarative graph layout and rendering engine compiled from Rust to
WebAssembly using wasm-pack
. It provides an API for creating and rendering
directed graphs and producing SVGs.
- 💾 Low memory footprint
- 🎉 No system dependencies
- 🚀 Fastest layout and rendering engine powered by WebAssembly
- 🔥 Works in any client / server configuration
- 🛠️ Create and manipulate directed graphs.
- 🔀 Handles multiple edges with the same
source
andtarget
nodes. - 🔄 Render cyclical directed graphs.
- 🎨 Support various custom rendering attributes for enhanced visualization.
Vizdom comes in several distributions:
-
esm
(Modules) -
node
(CJS) -
web
(Browser)
Simply select a distribution and install using your favorite package manager
following the saming convention @vizdom/vizdom-ts-<dist>
.
npm install @vizdom/vizdom-ts-esm
pnpm install @vizdom/vizdom-ts-esm
yarn install @vizdom/vizdom-ts-esm
bun install @vizdom/vizdom-ts-esm
In the most basic configuration, all you need is to provide labels for nodes and edges.
import { DirectedGraph } from "@vizdom/vizdom-ts-esm";
// ... or CJS
// const { DirectedGraph } = require("@vizdom/vizdom-ts-node");
// Create a new graph
const graph = new DirectedGraph();
// Add vertices
const v0 = graph.new_vertex({
render: {
label: "Hello",
},
});
const v1 = graph.new_vertex({
render: {
label: "World!",
},
});
// Add an edge between the vertices
graph.new_edge(v0, v1, {
render: {
label: "Foo Bar",
},
});
// Position the graph
const positioned = graph.layout();
// Finally, obtain to an SVG
await fs.writeFile("./graph.svg", positioned.to_svg().to_string());
Check out the basic example, which produces a graph that looks like:
Vizdom offers an online mode that effortlessly syncs your graph with your Vizdom account, eliminating the need for manual positioning and rendering. This ensures your graph is always up-to-date with your latest code changes.
To get started with syncing, sign up for an account.
Once you have an account, syncing your graph is straightforward. Simply add a few additional parameters when constructing your graph:
const graph = new DirectedGraph(
{}, // <-- Optional layout and render attributes.
// Must be specified as the second argument.
{
client_id: process.env.VIZDOM_CLIENT_ID || "",
client_secret: process.env.VIZDOM_CLIENT_SECRET || "",
graph_id: process.env.VIZDOM_GRAPH_ID || "",
}
);
By specifying these parameters when creating your graph, you can easily manage multiple graphs within the same project, each syncing independently.
Vizdom can be used as a pure layout engine to obtain positioning information, which is especially useful if you already have a method for rendering your graph.
To use Vizdom for layout purposes, you need to provide the bounding box
dimensions. If you are using the library in a browser context, you can retrieve
these dimensions by using methods like
getBoundingClientRect()
on the HTML element. Compute the shape_w
(width) and shape_h
(height) and
pass them as layout
parameters.
Additionally, there's an optional argument, compute_bounding_box
, which can be
set to false
. This tells the library to use the provided layout values for the
bounding box instead of computing it from the label
attribute. By default, the
shape is considered to be a Shape.Rectangle
for nodes and Shape.Plaintext
for edges (which is also a rectangle) and should remain unchanged in this
context.
Each vertex and edge requires an id
to map the JSON result correctly. The
resulting JSON string will include these IDs, enabling accurate mapping of the
nodes and edges.
// ...
const v0 = graph.new_vertex(
{
layout: {
shape_w: 10,
shape_h: 10,
},
render: {
id: "v0",
},
},
{
compute_bounding_box: false,
}
);
// ...
// Similarly, for an edge you have the same API.
const e0 = graph.new_edge(
v0,
v1,
{
layout: {
shape_w: 10,
shape_h: 10,
},
render: {
id: "e1",
},
},
{
compute_bounding_box: false,
}
);
// Position the graph
const positioned = graph.layout();
// Obtain the json instance
const json = positioned.to_json();
// Get a JS/TS Object adhereing to the `IJsonPosition` interface.
const jsonObj = json.to_obj();
// Or get the JSON string directly
const jsonString: string = json.to_string();
// const jsonStringPretty: string = json.to_string_pretty();
For a practical example, check out the json example, which generates a positional JSON string similar to this.
Vizdom supports several layout and rendering options for those who want more control over the appearance of their graphs.
const v0 = graph.new_vertex({
render: {
label: "Foo",
color: "#ff2f8e",
fill_color: "#ff2f8eaa",
shape: Shape.Triangle,
style: VertexStyle.Dashed,
},
});
Check out the style example, which produces a graph that looks like:
Vizdom offers partial support for the DOT language, commonly used for defining graphs. Most styling attributes are supported; however, please note that undirected diagrams are currently not parsed. Additionally, while DOT files may specify layout engines, Vizdom will always use its own layout engine for positioning.
The parser fully supports cluster
graphs, allowing for subgraph
IDs
with the cluster_
prefix or subgraph
statements containing a
cluster=true
attribute.
Unsupported styles are gracefully ignored, defaulting to safe visual options to ensure smooth rendering.
import { DotParser } from "@vizdom/vizdom-ts-esm";
// ... or CJS
// const { DotParser } = require("@vizdom/vizdom-ts-node");
// Create a new Dot Parser
const parser = new DotParser();
const dotGraph = parser.parse("digraph { a -> b }");
const directedGraph = dotGraph.to_directed();
const positioned = directedGraph.layout();
// Once positioned, the graph can be exported as SVG or JSON.
await fs.writeFile("./graph.svg", positioned.to_svg().to_string());
const jsonObj = positioned.to_json().to_obj();
console.log(
util.inspect(jsonObj, { showHidden: false, depth: null, colors: true })
);
Check out the dot example, which produces a graph that looks like:
You may also sync a parsed DOT to your Vizdom account by specifying the options like the following:
// ...
const parser = new DotParser({
client_id: process.env.VIZDOM_CLIENT_ID || "",
client_secret: process.env.VIZDOM_CLIENT_SECRET || "",
graph_id: process.env.VIZDOM_GRAPH_ID || "",
});
const dotGraph = parser.parse("digraph { a -> b }");
// Sync complete!
Once synced to Vizdom, there's no need to manually convert the parsed AST to a
directed graph (to_directed()
), apply layout (layout()
) nor rendering
(to_svg()
, to_json()
) methods. These processes are automatically handled in
the browser when viewing your graph.
You can visually compare two graphs with Vizdom. Ensure that the id
attributes
are set and unique to track changes effectively. The resulting graphs will be
annotated with a 'glow' effect to highlight differences:
- ❌ Removed elements (
id
no longer exists) are highlighted in red. - ✅ Added elements (
id
is new) are highlighted in green. - 🟧 Modified elements (
id
is the same, but other attributes have changed) are highlighted in orange.
Check out the diff example, which produces two graphs that look like:
and
Licensed under the Apache License, Version 2.0. See the LICENSE file or visit https://www.apache.org/licenses/LICENSE-2.0
This project makes use of third-party components, each with its own licensing terms:
-
Dagre.js: Some of the layout algorithms in this project are inspired by Dagre.js, which is licensed under the MIT License. The full license text can be found in the
third_party/dagre/LICENSE
file. -
NotoSans Font: The NotoSans font used in this project is licensed under the SIL Open Font License. The full license text can be found in the
third_party/noto_sans/LICENSE
file. -
Others: Can be found in the
third_party/licenses.html
file.
Please note that while Vizdom is freely available for use under the Apache License 2.0, the Rust WebAssembly binary included in this library is closed-source. You are free to use the library, but the source code for the Rust WebAssembly binary is not publicly available.