KinoSigmajs Examples
Section
A collection of examples showing how to render interactive graphs with Kino.Sigma.
Setup
Mix.install([
{:kino_sigmajs, path: Path.expand("..", __DIR__)},
{:yog_ex, "~> 0.98"}
])
Example 1: A simple graph from a keyword list
Yog.Generator.Classic.binary_tree(11)
|> Yog.node_count()
Example 2: Using the builder API
alias Kino.Sigma.Graph
graph =
Graph.new()
|> Graph.add_node("elixir", label: "Elixir", color: "#6e28d9", size: 12)
|> Graph.add_node("erlang", label: "Erlang", color: "#a90533", size: 10)
|> Graph.add_node("beam", label: "BEAM", color: "#f59e0b", size: 8)
|> Graph.add_node("livebook", label: "Livebook", color: "#3b82f6", size: 8)
|> Graph.add_edge("elixir", "erlang", label: "runs on")
|> Graph.add_edge("elixir", "beam", label: "compiles to")
|> Graph.add_edge("livebook", "elixir", label: "built with")
Kino.Sigma.render(graph,
layout: [type: :force_atlas_2, iterations: 120],
height: "500px"
)
Example 3: Pre-positioned nodes (no layout)
When nodes already have x and y, you can skip the force-directed layout entirely.
Kino.Sigma.render(
nodes: [
%{id: "origin", label: "Origin", x: 0, y: 0, color: "#3b82f6", size: 10},
%{id: "right", label: "Right", x: 1, y: 0, color: "#f43f5e", size: 8},
%{id: "top", label: "Top", x: 0, y: 1, color: "#10b981", size: 8},
%{id: "diagonal", label: "Diagonal", x: 1, y: 1, color: "#f59e0b", size: 8}
],
edges: [
%{source: "origin", target: "right"},
%{source: "origin", target: "top"},
%{source: "right", target: "diagonal"},
%{source: "top", target: "diagonal"}
],
layout: [type: :none],
height: "400px"
)
Example 4: A larger random graph
This is where Sigma.js shines compared to static SVG renderers.
node_count = 2000
edge_count = 4000
nodes =
for i <- 1..node_count do
%{
id: "node-#{i}",
label: "#{i}",
color: "#3b82f6",
size: 4
}
end
edges =
Stream.repeatedly(fn ->
source = :rand.uniform(node_count)
target = :rand.uniform(node_count)
{source, target}
end)
|> Stream.filter(fn {source, target} -> source != target end)
|> Stream.uniq()
|> Enum.take(edge_count)
|> Enum.map(fn {source, target} ->
%{source: "node-#{source}", target: "node-#{target}", size: 1}
end)
Kino.Sigma.render(
nodes: nodes,
edges: edges,
layout: [type: :force_atlas_2, iterations: 100],
height: "600px"
)
Example 5: Custom styling
nodes =
for i <- 1..8 do
%{
id: "n#{i}",
label: "Node #{i}",
color: (if rem(i, 2) == 0, do: "#f43f5e", else: "#3b82f6"),
size: 6 + rem(i, 4)
}
end
edges =
for i <- 1..7 do
%{
source: "n#{i}",
target: "n#{i + 1}",
color: "#94a3b8",
size: 2
}
end
Kino.Sigma.render(
nodes: nodes,
edges: edges,
layout: [type: :force_atlas_2, iterations: 100],
height: "400px"
)
Example 6: Rendering a Yog graph
If you build graphs with Yog, you can pass the graph directly to Kino.Sigma.render/1. Node data can be a string/atom label or a map with :label, :color, :size, :x, and :y.
graph =
Yog.directed()
|> Yog.add_node("elixir", %{label: "Elixir", color: "#6e28d9", size: 12})
|> Yog.add_node("erlang", %{label: "Erlang", color: "#a90533", size: 10})
|> Yog.add_node("beam", %{label: "BEAM", color: "#f59e0b", size: 8})
|> Yog.add_edge_ensure(from: "elixir", to: "erlang", with: 1)
|> Yog.add_edge_ensure(from: "elixir", to: "beam", with: 1)
Kino.Sigma.render(graph,
layout: [type: :force_atlas_2, iterations: 120],
height: "500px"
)
Example 7: Performance mode for large graphs
For graphs with thousands of nodes, labels and edge labels can slow down the renderer. Enable :performance mode and reduce ForceAtlas2 iterations to keep interactions smooth:
graph = Yog.Generator.Classic.binary_tree(10)
Kino.Sigma.render(graph,
layout: [
type: :force_atlas_2,
iterations: 50,
settings: %{barnesHutOptimize: true, slowDown: 5}
],
performance: true,
height: "800px"
)
Example 8: Downsampling and clustering
For graphs that are too large even in performance mode, you can downsample to a fixed number of nodes/edges or collapse nodes into clusters.
graph = Yog.Generator.Classic.barbell(100, 100)
# Render a random sample of 1,000 nodes and 2,000 edges
Kino.Sigma.render(graph,
performance: true,
height: "600px"
)
You can also collapse nodes by a shared attribute. Here we treat each node’s :color as its cluster:
nodes =
for i <- 1..1000 do
%{
id: "n#{i}",
label: "Node #{i}",
color: (if rem(i, 2) == 0, do: "#f43f5e", else: "#3b82f6"),
size: 4
}
end
edges =
for _ <- 1..2000 do
source = :rand.uniform(1000)
target = :rand.uniform(1000)
%{source: "node-#{source}", target: "node-#{target}", size: 1}
end
Kino.Sigma.render(
nodes: nodes,
edges: edges,
cluster_by: :color,
performance: true,
height: "600px"
)
Notes
- The widget loads Sigma.js, Graphology, and ForceAtlas2 from CDN using dynamic ES module imports. Internet access is required on first render.
- If you need offline use or bundled dependencies, open an issue on the repository.
-
:performancemode disables edge labels and hides labels/edges while panning or zooming. Pass:sigma_settingsto override any Sigma.js renderer setting. -
:max_nodesand:max_edgesrandomly sample the graph.:cluster_bycollapses nodes sharing the same value into a single meta-node and aggregates inter-cluster edges.