Visualization

verve.viz computes layouts and charts in Zig and renders them as SVG on the server. Pages are complete with JavaScript off; one island makes a graph interactive when you want it.

See it live: the viz example renders every chart type below plus the interactive graph.

Graphs

const nodes = [_]verve.viz.GraphNode{ .{ .id = "core", .label = "core" },};
const edges = [_]verve.viz.GraphEdge{ .{ .from = "core", .to = "ui" },};
const g = verve.viz.Graph{ .nodes = &nodes, .edges = &edges, .layout = .force };

const svg = verve.viz.renderGraph(ctx, g, .{
    .width = 640, .height = 420,
    .node_color = "#1f6feb", .edge_color = "#30363d", .label_color = "#f5f5f5",
});

Layouts: .force (converged force-directed), .tree, .radial, .dag (layered, with crossing-minimization sweeps and virtual-node edge routing). GraphOpts.edge_routing: .straight, .curved (Catmull-Rom), .orthogonal (Manhattan, rounded corners). dag_crossing_iterations = 0 turns the sweep off if you want to show why it exists.

graphPositions(ctx, g, opts) returns the computed positions — feed them to an island's props so client-side interaction starts exactly where SSR drew.

Charts

All server-side SVG, one call each:

barChart, stackedBarChart, groupedBarChart, lineChart, areaChart, scatterChart, pieChart (with inner_ratio for donuts), candlestickChart, boxPlotChart, heatmapChart, radarChart, violinChart, sankeyChart, treemapChart, chordChart.

const data = [_]verve.viz.Datum{ .{ .label = "Jan", .value = 12 },};
const chart = verve.viz.barChart(ctx, &data, .{ .width = 480, .height = 300 });

Scales (LinearScale, BandScale, LogScale, TimeScale) and Axis are public if you want custom scenes.

Live updates — wire deltas

Stream graph mutations to connected clients instead of re-sending the graph:

const ops = try verve.viz.diffGraphs(alloc, old_nodes, old_edges, new_nodes, new_edges);
const frame = try verve.viz.writeDeltaJson(&buf, seq, ops);
// publish on a push channel; clients apply ops in seq order

The shipped VizGraphInteractive island consumes these over SSE: a server publisher thread ticks the model and broadcasts {"seq":N,"ops":[…]} frames; the chunk applies deltas, resyncing via a pull snapshot when a gap is detected. Zoom, selection, and collapse all survive the stream.

Interactive graphs

renderGraphInteractive(ctx, g, opts) emits SVG annotated for the interaction layer — wrap it in the VizGraphInteractive island and you get wheel-zoom, drag-to-pan, node dragging with pointer capture, hover tooltips, click-select, and double-click subtree collapse. The element set is fixed at SSR; the chunk only mutates transforms.

Next: Animation.