# How Ghidra's Graph Framework Works for Visualization: JUNG-Based MVC Architecture Explained

> Explore Ghidra's graph framework, a JUNG-based MVC architecture for interactive visualization. Learn how it renders graphs with zooming and panning capabilities.

- Repository: [National Security Agency/ghidra](https://github.com/NationalSecurityAgency/ghidra)
- Tags: internals
- Published: 2026-03-04

---

**Ghidra's graph framework employs a model-view-controller architecture built on the JUNG library to render interactive visual graphs with Swing-based vertices, supporting zooming, panning, and satellite views through three distinct coordinate spaces.**

The National Security Agency's Ghidra reverse engineering platform renders complex program structures through a flexible graph framework built atop the Java Universal Network/Graph (JUNG) library. This architecture separates model, layout, and view concerns to deliver responsive, interactive visualizations capable of handling thousands of vertices while supporting custom vertex components and edge articulations.

## Core MVC Architecture

Ghidra's graph framework implements a strict model-view-controller pattern that abstracts JUNG's low-level rendering capabilities while exposing Swing-centric vertex models. The core pieces are located in `Ghidra/Framework/Graph/src/main/java/ghidra/graph/` and its subpackages.

### The Model Layer (VisualGraph)

The **Model** layer centers on `VisualGraph<V,E>` defined in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/VisualGraph.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/VisualGraph.java). This directed graph implementation maintains vertex locations, focus states, and selection sets while notifying registered listeners of changes through methods like `vertexLocationChanged()`. Concrete implementations such as `FunctionGraph` supply the underlying JUNG `Graph<V,E>` and associated layout algorithms.

### Layout Abstraction (VisualGraphLayout)

The **Layout** layer uses `VisualGraphLayout<V,E>` found in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/VisualGraphLayout.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/VisualGraphLayout.java). This extension of JUNG's `Layout` interface provides vertex positioning, reports articulated edge support via `usesEdgeArticulations()`, and optionally supplies custom edge renderers through `getEdgeRenderer()`. The framework supports force-directed, hierarchical, or radial layouts through this abstraction.

### View and Container Components

The **View** layer consists of `GraphViewer<V,E>` in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewer.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewer.java), which extends JUNG's `VisualizationViewer`. It installs custom renderers (`VisualEdgeRenderer`, `VisualVertexRenderer`) and pick support (`VisualGraphShapePickSupport`) that resolves clicks using actual vertex shapes rather than bounding boxes.

The **Container** layer is handled by `GraphComponent<V,E,G>` in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphComponent.java). This Swing container constructs the primary `GraphViewer`, manages the satellite view, wires mouse plugins, and handles layout changes. When disposed, `GraphComponent.dispose()` clears cached layouts and removes listeners to prevent memory leaks.

## Coordinate Space Transformations

Accurate mouse interaction requires translating coordinates between three distinct spaces, managed by `GraphViewerUtils` in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewerUtils.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewerUtils.java).

- **Layout Space**: Raw `Point2D` values produced by the layout algorithm.
- **Graph Space**: Layout points transformed by the current pan and zoom operations via JUNG's `MultiLayerTransformer` `LAYOUT` layer.
- **View Space**: Final Java 2D screen coordinates after view-layer scaling.

The static helper `translatePointFromViewSpaceToGraphSpace()` converts screen coordinates back to graph coordinates for vertex picking, while `translatePointFromLayoutSpaceToViewSpace()` handles the inverse operation for tool-tip positioning.

## Interaction and Event Handling

### Mouse Plugins and Selection

`GraphViewer` delegates input processing to `VisualGraphPluggableGraphMouse`, which contains specialized plugins:

- `VisualGraphHoverMousePlugin`: Handles hover highlighting and cursor changes.
- `VertexClickMousePlugin`: Processes double-click events on vertices.

Selection state is maintained through `GPickedState`. When a vertex is picked, `VertexPickingListener` implementations update the model via `graph.setVertexFocused()` or `graph.setSelectedVertices()`. Zoom operations can be configured as mouse-relative (default) or view-centered through `GraphViewer.useMouseRelativeZoom()`.

### Path Highlighting

The framework includes a `VisualGraphPathHighlighter` accessible through `GraphViewer.setVertexHoverPathHighlightMode()`. This component calculates and renders the shortest path from a focused vertex to all reachable vertices, enabling visual tracing of data flow or call chains.

## Satellite View Synchronization

`GraphComponent` automatically creates a **satellite view** using `SatelliteGraphViewer` located in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/satellite/SatelliteGraphViewer.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/satellite/SatelliteGraphViewer.java). This miniature overview displays the entire graph at reduced scale and may employ caching for large graphs.

Synchronization is handled by `VisualGraphViewUpdater` in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualGraphViewUpdater.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualGraphViewUpdater.java), which receives the satellite instance via `viewUpdater.setSatelliteViewer()`. The updater manages animated panning, zoom-to-fit operations, vertex "twinkle" effects, and ensures the satellite rectangle accurately reflects the primary view's viewport. Users can dock, undock, or pop out the satellite into independent windows.

## Practical Implementation Example

The following self-contained example demonstrates creating a simple directed graph visualization using Ghidra's framework. This mirrors the implementation pattern used by Ghidra's Function Graph plugin.

```java
import ghidra.graph.*;
import ghidra.graph.viewer.*;
import ghidra.graph.viewer.layout.*;
import ghidra.graph.viewer.options.*;
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import java.awt.*;
import javax.swing.*;

public class SimpleGraphDemo {

    /** Simple vertex that implements VisualVertex */
    static class SimpleVertex implements VisualVertex {
        private final String label;
        private Point location = new Point(0, 0);
        private boolean focused, selected;

        SimpleVertex(String s) { this.label = s; }
        @Override public JComponent getComponent() { return new JLabel(label); }
        @Override public void setLocation(Point p) { this.location = p; }
        @Override public Point getLocation() { return location; }
        @Override public void setFocused(boolean f) { this.focused = f; }
        @Override public boolean isFocused() { return focused; }
        @Override public void setSelected(boolean s) { this.selected = s; }
        @Override public boolean isSelected() { return selected; }
        // Boiler‑plate for VisualVertex …
        @Override public double getEmphasis() { return 0; }
        @Override public void setEmphasis(double e) {}
        @Override public Component getComponent() { return null; }
    }

    /** Simple edge – no extra data needed */
    static class SimpleEdge implements VisualEdge<SimpleVertex> {
        private final SimpleVertex start, end;
        private boolean selected, hovered;
        SimpleEdge(SimpleVertex s, SimpleVertex e) { start=s; end=e; }
        @Override public SimpleVertex getStart() { return start; }
        @Override public SimpleVertex getEnd()   { return end; }
        @Override public void setSelected(boolean b) { selected=b; }
        @Override public boolean isSelected() { return selected; }
        @Override public void setHovered(boolean b) { hovered=b; }
        @Override public boolean isHovered() { return hovered; }
        // Boiler‑plate …
        @Override public double getEmphasis() { return 0; }
        @Override public void setEmphasis(double e) {}
    }

    /** Concrete VisualGraph implementation */
    static class SimpleVisualGraph
            implements VisualGraph<SimpleVertex, SimpleEdge> {

        private final DirectedSparseGraph<SimpleVertex, SimpleEdge> g =
                new DirectedSparseGraph<>();

        private final VisualGraphLayout<SimpleVertex, SimpleEdge> layout;

        SimpleVisualGraph() {
            // Populate graph
            SimpleVertex a = new SimpleVertex("A");
            SimpleVertex b = new SimpleVertex("B");
            SimpleVertex c = new SimpleVertex("C");
            g.addVertex(a); g.addVertex(b); g.addVertex(c);
            g.addEdge(new SimpleEdge(a,b));
            g.addEdge(new SimpleEdge(b,c));
            g.addEdge(new SimpleEdge(c,a));

            // Use a force‑directed layout wrapped in a default implementation
            FRLayout<SimpleVertex, SimpleEdge> fr = new FRLayout<>(g);
            fr.setSize(new Dimension(800,600));
            layout = new DefaultVisualGraphLayout<>(g, fr); // see Ghidra source for wrapper
        }

        @Override public void vertexLocationChanged(SimpleVertex v, Point p,
                ChangeType ct) { v.setLocation(p); }
        @Override public SimpleVertex getFocusedVertex() { return null; }
        @Override public void setVertexFocused(SimpleVertex v, boolean b) {}
        @Override public void clearSelectedVertices() {}
        @Override public void setSelectedVertices(Set<SimpleVertex> s) {}
        @Override public Set<SimpleVertex> getSelectedVertices() { return Set.of(); }
        @Override public void addGraphChangeListener(VisualGraphChangeListener<SimpleVertex,SimpleEdge> l){}
        @Override public void removeGraphChangeListener(VisualGraphChangeListener<SimpleVertex,SimpleEdge> l){}
        @Override public VisualGraphLayout<SimpleVertex,SimpleEdge> getLayout() { return layout; }
        @Override public VisualGraph<SimpleVertex,SimpleEdge> copy() { return this; }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            SimpleVisualGraph graph = new SimpleVisualGraph();

            // GraphComponent is the high‑level container
            GraphComponent<SimpleVertex, SimpleEdge, SimpleVisualGraph> comp =
                    new GraphComponent<>(graph);

            // Optional: tweak colours
            VisualGraphOptions opts = new VisualGraphOptions();
            opts.setBackgroundColor(new GColor("color.bg.visualgraph"));
            comp.setGraphOptions(opts);

            JFrame f = new JFrame("Ghidra Graph Demo");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(comp.getComponent(), BorderLayout.CENTER);
            f.setSize(900,700);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}

```

**Key implementation steps**:

1. **Implement VisualVertex and VisualEdge**: `SimpleVertex` provides a Swing `JComponent` via `getComponent()` and tracks location, selection, and focus state. `SimpleEdge` connects vertices and maintains hover/selection flags.
2. **Create the VisualGraph**: `SimpleVisualGraph` builds a JUNG `DirectedSparseGraph`, populates it with vertices and edges, and wraps a force-directed `FRLayout` in a `DefaultVisualGraphLayout`.
3. **Instantiate GraphComponent**: The `GraphComponent` constructor creates the primary viewer, satellite view, and mouse plugins automatically.
4. **Configure Options**: `VisualGraphOptions` (defined in [`Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/service/graph/GraphDisplayOptions.java)) controls background colors, zoom behavior, and path highlighting modes.

## Summary

- Ghidra's graph framework extends the JUNG library with a **model-view-controller architecture** that separates graph data from rendering and interaction logic.
- **Three coordinate spaces** (Layout, Graph, and View) enable precise mouse interaction and vertex positioning, translated via `GraphViewerUtils`.
- **VisualGraph and VisualGraphLayout** interfaces allow developers to plug custom data structures into the visualization pipeline while reusing Ghidra's layout algorithms.
- **GraphComponent** provides a complete Swing container with satellite view, mouse plugins, and lifecycle management via `dispose()`.
- The framework supports **articulated edges**, custom vertex components (any Swing widget), and interactive path highlighting through `VisualGraphPathHighlighter`.

## Frequently Asked Questions

### What underlying library powers Ghidra's graph visualization framework?

The framework is built atop the **Java Universal Network/Graph (JUNG)** library, which provides the fundamental graph data structures and layout algorithms. Ghidra wraps JUNG with classes like `GraphViewer` (extending `VisualizationViewer`) and `VisualGraphLayout` to add Swing component support, coordinate transformations, and Ghidra-specific interaction models.

### How does the framework handle coordinate translation for mouse interactions?

The system maintains **three coordinate spaces**: Layout Space (raw algorithm output), Graph Space (post-pan/zoom transformation), and View Space (screen pixels). The `GraphViewerUtils` class provides static methods like `translatePointFromViewSpaceToGraphSpace()` to convert screen coordinates back to graph coordinates for vertex picking, ensuring accurate selection even when zoomed or panned.

### What is the satellite view and how does it stay synchronized with the main graph?

The **satellite view** is a miniature `SatelliteGraphViewer` instance created by `GraphComponent` that displays the entire graph at reduced scale. The `VisualGraphViewUpdater` maintains synchronization by updating the satellite's viewport rectangle whenever the primary view pans or zooms. It also manages animated transitions like "twinkle" effects and zoom-to-fit operations across both views.

### How can developers customize vertex appearance and behavior in Ghidra graphs?

Developers implement the `VisualVertex` interface (located in [`Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualVertex.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualVertex.java)) to return custom Swing components via `getComponent()`. This allows any Java Swing widget—from simple labels to complex code listings—to serve as a graph vertex. Additionally, implementing `VisualGraphLayout` enables custom edge shapes through `getEdgeShapeTransformer()` and articulated edge support via `usesEdgeArticulations()`.