# How the Architecture Renderer in Archify Compiles Diagrams: A Technical Deep Dive

> Explore how the Archify Architecture Renderer compiles diagrams client-side using a three-stage pipeline: JSON parsing, DOT conversion, and Viz.js SVG generation. No server needed.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: deep-dive
- Published: 2026-09-01

---

**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`](https://github.com/tt-a1i/archify/blob/main/.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`](https://github.com/tt-a1i/archify/blob/main/examples/rag-pipeline.architecture.json):

```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`](https://github.com/tt-a1i/archify/blob/main/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`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html), the entry point loads the JSON asynchronously:

```javascript
// 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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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`](https://github.com/tt-a1i/archify/blob/main/archify/package-lock.json).

### The Rendering Pipeline

```javascript
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`](https://github.com/tt-a1i/archify/blob/main/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:

```html
<!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`](https://github.com/tt-a1i/archify/blob/main/examples/web-app-rendered.html):

```javascript
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`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html)** — Demonstrates JSON fetching and renderer invocation
- **[`examples/web-app-rendered.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app-rendered.html)** — Contains rendered output and inline renderer integration
- **[`examples/rag-pipeline.architecture.json`](https://github.com/tt-a1i/archify/blob/main/examples/rag-pipeline.architecture.json)** — Reference architecture schema example
- **`archify/renderer.mjs`** — Core implementation (within `archify.zip` distribution)
- **[`archify/package-lock.json`](https://github.com/tt-a1i/archify/blob/main/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`](https://github.com/tt-a1i/archify/blob/main/.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`](https://github.com/tt-a1i/archify/blob/main/examples/web-app-rendered.html).