El Grapho is a high performance WebGL graph data visualization engine. El Grapho can support millions of interactive nodes and edges in any modern browser.
If you need to build a graph visualization for the web of any kind, such as a tree, force directed graph, network graph, etc., and scale and performance are important to you, then El Grapho is a great option. El Grapho was built to push the limits of modern browsers.
To get started, you can install El Grapho from npm like this
npm install --save elgrapho
or you can download the latest El Grapho distribution file found here
https://github.com/ericdrowell/ElGrapho/blob/master/engine/dist/ElGrapho.min.js
To create a simple El Grapho data visualization, you can instantiate a new graph like this
let graph = new ElGrapho({
container: document.getElementById('container'),
model: {
nodes: [
{x: 0, y: 0.6, group: 0, label: 0},
{x: -0.4, y: 0, group: 1, label: 1},
{x: 0.4, y: 0, group: 1, label: 2},
{x: -0.6, y: -0.6, group: 2, label: 3},
{x: -0.2, y: -0.6, group: 2, label: 4},
{x: 0.2, y: -0.6, group: 2, label: 5},
{x: 0.6, y: -0.6, group: 2, label: 6}
],
edges: [
{from: 0, to: 1},
{from: 0, to: 2},
{from: 1, to: 3},
{from: 1, to: 4},
{from: 2, to: 5},
{from: 2, to: 6}
]
},
width: 500,
height: 500
});
-
container
- DOM element that will contain the El Grapho graph. -
model.nodes
- object that defines the nodes in the graph. Each node is defined by a position (x and y), and also a group. El Grapho x and y ranges are between -1 and 1. If x is -1, then the node position is on the very left of the viewport. If x is 0 it is in the center, and if x is 1 it is on the very right of the viewport. Groups are integer values, and El Grapho automatically assigns colors to each unique grouping. To add labels, simply add alabel
key and value pair to each node. -
model.edges
- object that defines the edges between nodes based on their indices. Each edge is defined by a from-node-index and a to-node-index. In the example above, the first edge begins at node0
and ends at node1
. For non directed graphs, or bi-directional graphs,from
andto
are interchangeable. -
width
- number that defines the width of the El Grapho viewport in pixels. The default is500
. -
height
- number defines the height of the El Grapho viewport in pixels. The default is500
. -
nodeSize
- number between 0 and 1 which defines the node size. The default is1
. -
nodeOutline
- boolean that enables or disables node outlines. The default istrue
. -
edgeSize
- number between 0 and 1 which defines the edge size. Edge sizes are relative to the connecting node size. The default is0.25
. -
darkMode
- boolean that enables or disables dark mode. The default isfalse
. -
glowBlend
- number between 0 and 1 that defines the glow blending of nodes and edges. A value of 0 has no glow blending, and a value of 1 has full glow blending. Glow blending can be used as a visual treatment to emphasize node clustering or edge bundling. It is most effective when used in conjunction with dark mode. The default is0
. -
fillContainer
- boolean that enables or disables auto filling the container. When true, El Grapho will automatically detect anytime its container has changed shape, and will auto resize itself. The default isfalse
. -
tooltips
- boolean that enables or disables tooltips. The default istrue
. -
arrows
- boolean that enables or disables edge arrows. For non directed or bi-directional graphs, you should keeparrows
asfalse
. The default isfalse
. -
animations
- boolean that defines animation strategy. When animations is true, zoom and pan transitions will be animated. Otherwise the transitions will be immediate. Although animations utilize requestAnimationFrame for dynamic frame rates, in some situations you may prefer to set animations to false to improve transition performance for very high cardinality graphs with millions of nodes and edges. The default istrue
. -
debug
- boolean that can be used to enable debug mode. Debug mode will show the node and edge count in the bottom right corner of the visualization. The default isfalse
.
Determining the positions of the nodes for your graph can be a lot of work! While it's nice to have the power to construct custom graph layouts, most El Grapho users will want to leverage the provided El Grapho layouts which will generate node positions for you. Currently, El Grapho supports ForceDirected
, Tree
, RadialTree
, Hairball
, Chord
, and Cluster
let model = {
nodes: [
{group: 0},
{group: 1},
{group: 1},
{group: 2},
{group: 2},
{group: 3}
],
edges: [
{from: 0, to: 1},
{from: 0, to: 2},
{from: 1, to: 3},
{from: 1, to: 4},
{from: 2, to: 5},
],
steps: 30
};
graph = new ElGrapho({
container: document.getElementById('container'),
model: ElGrapho.layouts.ForceDirected(model),
width: 500,
height: 500
});
The ForceDirected
layout uses d3-force to reduce edge crossing and optimize node spacing. Although the algorithm is fairly efficient with a runtime of O(nlog(n)) via the Barnes-Hut approximation which uses quad trees, this can still be slow for very large graphs of 50k nodes or more. If it's possible to build your models in advance, it's a good idea to build the force directed graph model on the server and then cache it. If you require your models to be constructed at execution time, and the number of nodes is very high, you may consider using an O(n) model substitude such as HairBall
or Cluster
The ForceDirected
layout accepts a steps
property from the model config which can be used to define the accuracy of the result. This is because force directed graphs require multiple passes to obtain a final result. The default is 30. Lowering this number will result in faster model construction but less accurate results. Increasing this number will result in slower model construction but more accurate results.
let model = {
nodes: [
{group: 0},
{group: 1},
{group: 1},
{group: 2},
{group: 2},
{group: 3},
{group: 3}
],
edges: [
{from: 0, to: 1},
{from: 0, to: 2},
{from: 1, to: 3},
{from: 1, to: 4},
{from: 2, to: 5},
{from: 2, to: 6}
]
};
let graph = new ElGrapho({
container: document.getElementById('container'),
model: ElGrapho.layouts.Tree(model),
width: 500,
height: 500
});
let model = {
nodes: [
{group: 0},
{group: 1},
{group: 1},
{group: 2},
{group: 2},
{group: 3},
{group: 3}
],
edges: [
{from: 0, to: 1},
{from: 0, to: 2},
{from: 1, to: 3},
{from: 1, to: 4},
{from: 2, to: 5},
{from: 2, to: 6}
]
};
let graph = new ElGrapho({
container: document.getElementById('container'),
model: ElGrapho.layouts.RadialTree(model),
width: 500,
height: 500
});
let model = {
nodes: [
{group: 0},
{group: 1},
{group: 1},
{group: 2},
{group: 2},
{group: 3},
{group: 3}
],
edges: [
{from: 0, to: 1},
{from: 0, to: 2},
{from: 1, to: 3},
{from: 1, to: 4},
{from: 2, to: 5},
{from: 2, to: 6}
]
};
let graph = new ElGrapho({
container: document.getElementById('container'),
model: ElGrapho.layouts.Hairball(model),
width: 500,
height: 500
});
The Hairball
layout is an excellent alternative to ForceDirected
layouts when the number of nodes is very high, for example in the millions. This is because it runs in O(n) time, i.e. linear time. Essentially, nodes with lots of edges are forced towards the center, and nodes with few edges are pushed out to the perimeter, creating a hairball effect.
let model = {
nodes: [
{group: 0},
{group: 1},
{group: 1},
{group: 2},
{group: 2},
{group: 3},
{group: 3}
],
edges: [
{from: 0, to: 1},
{from: 0, to: 2},
{from: 1, to: 3},
{from: 1, to: 4},
{from: 2, to: 5},
{from: 2, to: 6}
]
};
let graph = new ElGrapho({
container: document.getElementById('container'),
model: ElGrapho.layouts.Cluster(model),
arrows: true,
width: 500,
height: 500
});
The Cluster
layout clusters nodes by group. If a single group is used for all of the nodes, El Grapho will generate a single centered cluster. If there are several groups used, El Grapho will render distinct clusters. Because Cluster layouts can be generated in O(n)
time, i.e. linear time, they are another great alternative to ForceDirected
if performance becomes an issue.
let model = {
nodes: [
{group: 0},
{group: 1},
{group: 1},
{group: 2},
{group: 2},
{group: 3},
{group: 3}
],
edges: [
{from: 0, to: 1},
{from: 0, to: 2},
{from: 1, to: 3},
{from: 1, to: 4},
{from: 2, to: 5},
{from: 2, to: 6}
]
};
let graph = new ElGrapho({
container: document.getElementById('container'),
model: ElGrapho.layouts.Chord(model),
width: 500,
height: 500
});
You may have noticed that the layout config schema is identical for all layouts. As a result, El Grapho visualizations are polymorphic, meaning you can pass the same data structure into different layouts and get different graphs. Pretty cool!
Because the El Grapho layouts are fully decoupled from the rendering engine itself, they can be executed on the client or on the server, depending on your needs. For really complex layouts, it may be best to build the model on the server, deliver the output over http to the browser, and pass it directly into the El Grapho config.
El Grapho has controls in the upper right corner of the visualization that enable users to navigate large and complex graphs. These controls appear when you mouseover the visualization. There are three modes:
- select - use this mode to select nodes
- zoom - use this mode to draw zoom boxes around areas of interest or to zoom into a particular region of the graph
- pan - use this mode to pan the visualization around
And there are three action buttons:
- reset
- zoom in
- zoom out
El Grapho ships with a default template and default content. It is assumed however that you will be providing your own tooltip content (at the end of the day, most people want something custom anyways). To set the tooltip template, simply do the following:
let graph = new ElGrapho(config);
graph.tooltipTemplate = function(index, el) {
el.innerHTML = 'node index: ' + index;
};
This means that your tooltips can be anything! You can show lots of information about the node, insert images, etc. The tooltip template is decoupled from the El Grapho config in order to ensure that the config is serializable and thus transferable over http.
To disable tooltips entirely, you can set the tooltips
property to false in the graph config.
El Grapho also has a built in event bus. Currently, the supported events are:
idle
node-mouseover
node-mouseout
node-click
and you can use these events like this:
let graph = new ElGrapho(config);
graph.on('idle', function() {
console.log('idle');
});
graph.on('node-mouseover', function(evt) {
console.log('node-mouseover: ' + evt.dataIndex);
});
-
graph.selectGroup(index)
- select a group by index, whereindex
is aninteger
-
graph.deselectGroup()
- deselect the currently selected group -
graph.setSize(width, height)
- set the graph size in pixels, wherewidth
andheight
is anumber
-
graph.setDarkMode(mode)
- enable or disable dark mode, wheremode
is aboolean
-
graph.selectNode(index)
- select node by index, whereindex
is aninteger
-
graph.deselectNode()
- deselect a selected node
Why is this called El Grapho? - Why not?
Okay... and why is the logo a skeleton with a rose in his mouth? - Why not?