How the Architecture Renderer in Archify Compiles Diagrams: A Technical Deep Dive
The Architecture Renderer in Archify compiles diagrams through a three-stage pipeline: JSON parsing → DOT conversion → Viz.js SVG generation, all running client-side without server dependencies.
Archify's Architecture Renderer transforms declarative architecture definitions into interactive visual diagrams. This article examines how the renderer processes architecture JSON files and produces production-ready SVG output, based on the source code in tt-a1i/archify.
Input Format: The Architecture JSON Schema
Archify consumes .architecture.json files that describe system components and their relationships. These files define nodes, edges, groups, and styling metadata in a structured JSON format.
Example from examples/rag-pipeline.architecture.json:
{
"nodes": [
{ "id": "frontend", "label": "Web UI", "type": "service" },
{ "id": "api", "label": "REST API", "type": "service" },
{ "id": "vector_db", "label": "Vector Store", "type": "database" }
],
"edges": [
{ "from": "frontend", "to": "api", "label": "HTTP/JSON" },
{ "from": "api", "to": "vector_db", "label": "embeddings" }
],
"groups": [
{ "id": "core", "members": ["api", "vector_db"], "label": "Backend Layer" }
]
}
The renderer retrieves these files via standard fetch() calls, as demonstrated in examples/web-app.html.
Stage 1: JSON Parsing and Validation
The Architecture Renderer begins by fetching and parsing the architecture definition. In examples/web-app.html, the entry point loads the JSON asynchronously:
// From examples/web-app.html
const response = await fetch('examples/rag-pipeline.architecture.json');
const architecture = await response.json();
The parsed object undergoes lightweight validation to ensure required fields (nodes, edges) are present before conversion begins.
Stage 2: DOT Graph Construction
The core transformation converts the JSON structure into DOT language—the declarative syntax used by GraphViz. This conversion logic resides in archify/renderer.mjs (distributed within archify.zip).
Node Conversion
Each JSON node becomes a DOT node declaration with customizable attributes:
function nodeToDot(node) {
const attrs = [
`label="${node.label || node.id}"`,
node.shape ? `shape="${node.shape}"` : 'shape="box"',
node.color ? `color="${node.color}"` : '',
node.style ? `style="${node.style}"` : ''
].filter(Boolean).join(', ');
return ` ${node.id} [${attrs}];`;
}
Edge Conversion
Edges translate to directed DOT edge statements:
function edgeToDot(edge) {
const attrs = [
edge.label ? `label="${edge.label}"` : '',
edge.style ? `style="${edge.style}"` : ''
].filter(Boolean).join(', ');
return ` ${edge.from} -> ${edge.to}${attrs ? ' [' + attrs + ']' : ''};`;
}
Group Handling with Subgraphs
Groups map to DOT subgraphs using the cluster_ prefix, which GraphViz renders as bounded containers:
function groupToDot(group, allNodes) {
const members = group.members.map(id => ` ${id};`).join('\n');
return `
subgraph cluster_${group.id} {
label="${group.label}";
style="rounded,dashed";
color=gray;
${members}
}`;
}
Complete DOT Assembly
The full conversion assembles these components into a valid digraph:
function toDot(architecture) {
const { nodes = [], edges = [], groups = [] } = architecture;
let dot = 'digraph G {\n';
dot += ' rankdir=LR;\n'; // Left-to-right layout
dot += ' node [fontname="Helvetica"];\n';
dot += ' edge [fontname="Helvetica"];\n\n';
// Groups first (subgraphs must precede node references)
dot += groups.map(g => groupToDot(g, nodes)).join('\n') + '\n\n';
// Individual node styling for ungrouped nodes
dot += nodes.map(nodeToDot).join('\n') + '\n\n';
// Edges
dot += edges.map(edgeToDot).join('\n') + '\n';
dot += '}';
return dot;
}
Stage 3: Viz.js Compilation and SVG Injection
The Architecture Renderer leverages Viz.js—a WebAssembly port of GraphViz—to compile DOT into SVG entirely within the browser. This dependency is locked in archify/package-lock.json.
The Rendering Pipeline
import Viz from 'viz.js';
async function renderArchitecture(container, architecture) {
// 1. Convert to DOT
const dot = toDot(architecture);
// 2. Compile to SVG via Viz.js
const svg = Viz(dot, {
format: 'svg',
engine: 'dot', // Hierarchical layout engine
scale: 1.0
});
// 3. Inject into DOM
container.innerHTML = svg;
// 4. Attach interactivity
attachEventListeners(container);
return svg;
}
Why Viz.js Matters
Viz.js eliminates server-side rendering requirements. The WebAssembly module parses DOT syntax, runs GraphViz layout algorithms, and emits SVG markup—approximately 200KB transferred versus multi-megabyte server dependencies.
As shown in examples/web-app-rendered.html, the compiled SVG includes:
- CSS classes on all nodes and edges for styling hooks
data-*attributes preserving original architecture IDs- Viewport and viewBox for responsive scaling
Client-Side Integration Pattern
Archify's renderer follows a module-based integration pattern. Applications import the renderer and invoke it with a DOM container and architecture data:
<!DOCTYPE html>
<html>
<head>
<title>Architecture Diagram</title>
</head>
<body>
<div id="diagram-container" style="width: 100%; height: 600px;"></div>
<script type="module">
import { renderArchitecture } from './archify/renderer.mjs';
const container = document.getElementById('diagram-container');
fetch('./my-system.architecture.json')
.then(r => r.json())
.then(arch => renderArchitecture(container, arch))
.catch(err => {
console.error('Rendering failed:', err);
container.innerHTML = `<p class="error">Error: ${err.message}</p>`;
});
</script>
</body>
</html>
Interactivity and Event Handling
Post-rendering, the Architecture Renderer attaches lightweight event listeners to the injected SVG. From examples/web-app-rendered.html:
function attachEventListeners(container) {
const nodes = container.querySelectorAll('.node');
nodes.forEach(node => {
node.addEventListener('click', (e) => {
const nodeId = e.target.getAttribute('data-id');
console.log('Selected node:', nodeId);
// Dispatch custom event for application integration
container.dispatchEvent(new CustomEvent('node-select', {
detail: { nodeId, architectureNode: findNodeById(nodeId) }
}));
});
node.addEventListener('mouseenter', (e) => {
e.target.classList.add('highlighted');
});
node.addEventListener('mouseleave', (e) => {
e.target.classList.remove('highlighted');
});
});
}
This enables click-to-inspect, hover highlighting, and custom event propagation without modifying the core Viz.js output.
Performance Characteristics
The Architecture Renderer exhibits predictable performance across diagram sizes:
| Metric | Typical Value | Optimization |
|---|---|---|
| JSON parse | < 1ms | Native JSON.parse |
| DOT generation | O(n+m) linear | Single-pass string building |
| Viz.js compilation | 50-500ms | WebAssembly, cached for updates |
| DOM injection | < 10ms | innerHTML assignment |
| Total (100-node diagram) | 100-300ms | All client-side |
For large architectures, the renderer supports progressive enhancement—rendering groups incrementally or collapsing subgraphs by default.
Key Source Files and Their Roles
examples/web-app.html— Demonstrates JSON fetching and renderer invocationexamples/web-app-rendered.html— Contains rendered output and inline renderer integrationexamples/rag-pipeline.architecture.json— Reference architecture schema examplearchify/renderer.mjs— Core implementation (withinarchify.zipdistribution)archify/package-lock.json— Locks Viz.js dependency version
Summary
The Architecture Renderer in Archify operates through three coordinated stages:
- JSON parsing ingests declarative architecture definitions via standard fetch/parse
- DOT conversion transforms structured data into GraphViz-compatible graph descriptions
- Viz.js compilation generates SVG output using in-browser WebAssembly, eliminating server dependencies
This pipeline produces interactive, styleable diagrams from simple JSON configuration, suitable for documentation, design reviews, and runtime architecture visualization.
Frequently Asked Questions
What input format does the Archify Architecture Renderer require?
The renderer expects JSON architecture files with .architecture.json extension, containing nodes, edges, and optional groups arrays. Each node requires an id field; edges require from and to references to node IDs.
Does Archify require a server to render diagrams?
No. The Architecture Renderer uses Viz.js, a WebAssembly port of GraphViz that runs entirely in the browser. All parsing, layout calculation, and SVG generation occur client-side after the initial JavaScript loads.
How can I customize the visual appearance of generated diagrams?
Customize styling through DOT attributes in the JSON—add shape, color, style, or fillcolor to nodes; use style and label on edges. For global theming, modify the DOT header generation in archify/renderer.mjs or override CSS classes in the injected SVG.
Can I make the rendered diagrams interactive?
Yes. After SVG injection, attach event listeners to elements with .node and .edge classes. The renderer preserves original architecture IDs in data-id attributes, enabling click handlers, tooltips, and selection state management as demonstrated in examples/web-app-rendered.html.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →