# How Graph Visualization Works in code-review-graph: A Deep Dive into the VS Code Extension

> Explore how code-review-graph visualizes code reviews using D3.js and SQLite within VS Code. Learn about interactive graph features for efficient code navigation.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: deep-dive
- Published: 2026-08-17

---

**The code-review-graph extension renders an interactive D3.js force-directed graph inside a VS Code Webview, passing data from a SQLite backend through message passing to enable zooming, dragging, and file navigation.**

Graph visualization in **code-review-graph** lets developers explore code-review relationships—dependencies, review history, and blast-radius impact—directly inside their editor. This article explains the complete architecture, from data preparation to rendering and interaction, based on the actual implementation in the `tirth8205/code-review-graph` repository.

## Data Preparation and Message Passing

The visualization pipeline begins in the backend. The extension gathers graph information—nodes, links, and metadata—from the **SQLite store** via [`src/backend/sqlite.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/backend/sqlite.ts) and from on-demand analysis features like `blastRadius`, `search`, and `reviewAssistant`.

When the user triggers the graph view, the extension creates a Webview panel and transmits the prepared data:

```typescript
// src/extension.ts – command registration
context.subscriptions.push(
  vscode.commands.registerCommand('codeReviewGraph.showGraph', () => {
    const panel = vscode.window.createWebviewPanel(
      'codeReviewGraph',
      'Code Review Graph',
      vscode.ViewColumn.One,
      { enableScripts: true }
    );

    // Load the HTML (which pulls in graph.ts)
    panel.webview.html = getWebviewContent();

    // Send the prepared graph data
    panel.webview.postMessage({
      type: 'graphData',
      payload: await buildGraphData()
    });
  })
);

```

The `buildGraphData()` function (implemented in [`src/backend/sqlite.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/backend/sqlite.ts)) serializes nodes and edges into a JSON payload. This payload travels through VS Code's `webview.postMessage()` API to the frontend.

## Webview Rendering with D3.js

The entry point for rendering is [`src/webview/graph.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/webview/graph.ts). This file receives the message, parses the payload, and constructs D3 data structures.

```typescript
window.addEventListener('message', ({ data }) => {
  if (data.type !== 'graphData') return;

  const { nodes, links } = data.payload;

  const svg = d3.select('#graph-area')
                .append('svg')
                .attr('width', width)
                .attr('height', height);

  const simulation = d3.forceSimulation(nodes)
      .force('link', d3.forceLink(links).id(d => d.id).distance(120))
      .force('charge', d3.forceManyBody().strength(-300))
      .force('center', d3.forceCenter(width / 2, height / 2))
      .force('collide', d3.forceCollide(40));

  // … render links, nodes, behaviours, and start the tick loop
});

```

The simulation combines four forces:

- **`forceLink`** – Controls link distance (120px) and strength between connected nodes
- **`forceManyBody`** – Applies repulsive charge (-300 strength) to spread nodes apart
- **`forceCenter`** – Pulls the graph toward the viewport center
- **`forceCollide`** – Prevents node overlap with a 40px collision radius

SVG elements are appended to `<div id="graph-area">`. Nodes render as `<circle>` or `<image>` elements with text labels; links render as `<line>` elements.

## User Interactions

The graph visualization supports four core interaction modes:

### Zoom and Pan

**`d3.zoom`** attaches to the SVG container, allowing users to scale and translate the entire graph. The zoom transform applies to a group element wrapping all visual elements, keeping coordinate calculations simple.

### Dragging

**`d3.drag`** updates node positions during pointer movement. The drag handler calls `simulation.alphaTarget(0.3).restart()` to heat up the simulation while dragging, ensuring connected nodes respond dynamically. On drag end, `alphaTarget(0)` lets the simulation settle.

### Hover and Tooltips

Mouse events on nodes trigger tooltip displays showing file paths, authors, and review statistics. These tooltips are HTML `<div>` elements positioned absolutely over the SVG, populated from node metadata in the D3 data join.

### Double-Click Navigation

Double-clicking a node sends a message back to the extension host:

```typescript
node.on('dblclick', (event, d) => {
  vscode.postMessage({ type: 'openFile', path: d.filePath });
});

```

The extension receives this in [`src/features/navigation.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/features/navigation.ts) and executes `vscode.open` or definition lookup commands.

## Responsive Layout and Cleanup

A **ResizeObserver** watches `#graph-area` for dimension changes. When the panel resizes, the handler:

1. Updates SVG `width` and `height` attributes
2. Recalculates the `forceCenter` position
3. Calls `simulation.alpha(0.3).restart()` to re-center the graph

When the Webview disposes, the simulation stops via `simulation.stop()` and all event listeners detach to prevent memory leaks.

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`src/webview/graph.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/webview/graph.ts) | Core D3 visualization, simulation setup, and interaction handling |
| [`src/views/graphWebview.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/views/graphWebview.ts) | Webview panel creation and HTML/JS bundle injection |
| [`src/extension.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/extension.ts) | Command registration and backend-to-Webview message wiring |
| [`src/backend/sqlite.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/backend/sqlite.ts) | Graph persistence and `buildGraphData()` implementation |
| [`src/features/blastRadius.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/features/blastRadius.ts) | Impact radius calculation for sub-graph highlighting |
| [`src/features/navigation.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/features/navigation.ts) | File opening and definition jump commands from graph UI |

## Summary

- **code-review-graph** uses a VS Code Webview with D3.js for force-directed graph visualization
- Data flows from SQLite through `postMessage()` to [`src/webview/graph.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/webview/graph.ts)
- The simulation combines link, charge, center, and collision forces for stable layouts
- Four interaction modes—zoom, drag, hover, and double-click—enable full exploration
- ResizeObserver and proper cleanup ensure responsive, memory-efficient operation

## Frequently Asked Questions

### What D3.js version does code-review-graph use?

The repository uses D3 v7 modules imported via ES modules. Individual forces (`d3-force`) and behaviors (`d3-zoom`, `d3-drag`) are imported selectively to minimize bundle size in the Webview context.

### Can the graph handle large codebases?

The force simulation scales to several hundred nodes with acceptable performance. For larger graphs, the `blastRadius` feature in [`src/features/blastRadius.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/features/blastRadius.ts) filters nodes to show only relevant dependencies, reducing the rendered set.

### How do I customize the graph appearance?

Modify [`src/webview/graph.ts`](https://github.com/tirth8205/code-review-graph/blob/main/src/webview/graph.ts) to adjust force parameters, node sizes, or color schemes. The D3 data join pattern makes it straightforward to rebind styles based on node metadata like review count or file type.

### Why does the graph re-center when I resize the panel?

The `forceCenter` force continuously pulls nodes toward the viewport middle. When dimensions change, the ResizeObserver restarts the simulation with updated center coordinates, causing the graph to drift toward the new center—this is intentional behavior for visibility.