<div id="sigma-container"></div>
import { layerBorder } from "@sigma/node-border";
import Graph from "graphology";
import Sigma from "sigma";
import { DEFAULT_STYLES } from "sigma/types";
import { registerControls } from "../_controls";
const container = document.getElementById("sigma-container") as HTMLElement;
const { bgColor, useColorAlpha, nodeOpacity, edgeOpacity, edgeSize } = registerControls({
{ label: "White", value: "#ffffff" },
{ label: "Light gray", value: "#e0e0e0" },
{ label: "Dark gray", value: "#333333" },
{ label: "Black", value: "#000000" },
useColorAlpha: { type: "boolean", label: "Use color alpha (not opacity)", default: false },
nodeOpacity: { type: "number", label: "Node opacity", default: 0.8, min: 0, max: 1, step: 0.05 },
edgeOpacity: { type: "number", label: "Edge opacity", default: 0.1, min: 0, max: 1, step: 0.05 },
edgeSize: { type: "number", label: "Edge size", default: 25, min: 0, max: 100, step: 2.5 },
container.style.backgroundColor = bgColor;
const isDarkBg = bgColor === "#333333" || bgColor === "#000000";
const res = await fetch("/data/wikipedia.json");
nodes: { key: string; label: string; cluster: string; x: number; y: number; score: number }[];
edges: [string, string][];
clusters: { key: string; color: string; clusterLabel: string }[];
// Build cluster color map
const clusterColors: Record<string, string> = {};
for (const c of data.clusters) {
clusterColors[c.key] = c.color;
// Brighten a hex color by mixing it with white
function brighten(hex: string, amount = 0.5): string {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
const mix = (c: number) => Math.round(c + (255 - c) * amount);
return `#${mix(r).toString(16).padStart(2, "0")}${mix(g).toString(16).padStart(2, "0")}${mix(b).toString(16).padStart(2, "0")}`;
// Convert hex color to rgba with given alpha
function withAlpha(hex: string, alpha: number): string {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
const graph = new Graph();
for (const node of data.nodes) {
const color = clusterColors[node.cluster] || "#999";
const nodeColor = useColorAlpha ? withAlpha(color, nodeOpacity) : color;
const fillColor = brighten(color, 0.5);
graph.addNode(node.key, {
fillColor: useColorAlpha ? withAlpha(fillColor, nodeOpacity) : fillColor,
// Add edges, colored by source node
for (const [source, target] of data.edges) {
if (graph.hasNode(source) && graph.hasNode(target)) {
const sourceColor = clusterColors[data.nodes.find((n) => n.key === source)?.cluster ?? ""] || "#999";
graph.addEdge(source, target, {
color: useColorAlpha ? withAlpha(sourceColor, edgeOpacity) : sourceColor,
new Sigma(graph, container, {
fillColor: { type: "color", default: "#ccc" },
{ size: 0.15, color: { attribute: "color" }, mode: "relative" },
{ size: 0, color: { attribute: "fillColor" }, fill: true },
opacity: useColorAlpha ? 1 : nodeOpacity,
size: { attribute: "score", min: 25, max: 100, minValue: 0, maxValue: 0.1 },
...(isDarkBg ? { labelColor: { whenState: "isHovered", then: "#000", else: "#fff" } } : {}),
edges: [DEFAULT_STYLES.edges, { opacity: useColorAlpha ? 1 : edgeOpacity, size: edgeSize }],