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 orderThe 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.