Creating Interactive Tensor-Shape Diagrams for GNN Articles
Technical articles about graph neural networks often fail readers at the
visualization step. A prose description of "a tensor of shape [N, C, H, W]
aggregated over neighbors via scatter" is hard to hold in working memory.
A well-designed diagram makes the same information immediately graspable.
This tutorial covers static-site-safe visualization techniques: SVG boxes for
precise diagrams, ASCII art for Markdown compatibility, and HTML details /
summary for lightweight interactivity. No heavy JavaScript frameworks, no
external dependencies.
Why Diagrams Are Hard to Get Right for GNNs
GNN diagrams need to communicate three things simultaneously:
1. Tensor shapes: how many dimensions, what each dimension means
2. Graph structure: which nodes connect to which edges
3. Data flow: how tensors transform through message passing
Most diagram tools optimize for one of these and ignore the others. The
approach here is to design for the reader's mental model rather than for
visual elegance.
Approach 1: ASCII Tensor Diagrams (Markdown-safe)
ASCII diagrams render everywhere: GitHub, static site generators, email,
terminal. They have zero dependencies. The trade-off is limited precision
for complex layouts.
Node feature tensor [N, C, H, W]:
node_features tensor shape: [N=6, C=3, H=16, W=16]
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ n₀ │ │ n₁ │ │ n₂ │ │ n₃ │ │ n₄ │ │ n₅ │
│3×16 │ │3×16 │ │3×16 │ │3×16 │ │3×16 │ │3×16 │
│×16 │ │×16 │ │×16 │ │×16 │ │×16 │ │×16 │
└─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘
↑ ↑ ───── N nodes, each holding [C,H,W] ─────
Edge index tensor [2, E]:
edge_index shape: [2, E=8]
row 0 (src): [0, 0, 1, 2, 2, 3, 4, 5]
row 1 (dst): [1, 2, 3, 3, 4, 5, 5, 0]
↑ ↑
edge 0 edge 7
Message passing data flow:
Aggregation at node n₃:
n₁ ──(e₂)──► n₃ ◄──(e₃)── n₂
↑
[message from n₁] + [message from n₂]
↓
scatter_reduce (sum/mean)
↓
agg_feature[n₃] shape: [C, H, W]
Approach 2: Inline SVG for Precise Diagrams
For HTML-rendered articles (Markdown with HTML support, Jekyll, Hugo, etc.),
inline SVG gives pixel-precise control with no external dependencies:
<!-- Tensor shape box diagram -->
<svg width="500" height="120" xmlns="http://www.w3.org/2000/svg"
role="img" aria-label="Tensor shape diagram for node_features [N, C, H, W]">
<!-- Background -->
<rect width="500" height="120" fill="#f8f9fa" rx="8"/>
<!-- Title -->
<text x="10" y="22" font-family="monospace" font-size="13" fill="#333">
node_features shape: [N, C, H, W]
</text>
<!-- Node boxes -->
<rect x="10" y="35" width="60" height="60" fill="#d0e8ff" stroke="#4a90e2" rx="4"/>
<text x="40" y="58" text-anchor="middle" font-size="11" fill="#333">n₀</text>
<text x="40" y="74" text-anchor="middle" font-size="10" fill="#666">[C,H,W]</text>
<rect x="80" y="35" width="60" height="60" fill="#d0e8ff" stroke="#4a90e2" rx="4"/>
<text x="110" y="58" text-anchor="middle" font-size="11" fill="#333">n₁</text>
<text x="110" y="74" text-anchor="middle" font-size="10" fill="#666">[C,H,W]</text>
<rect x="150" y="35" width="60" height="60" fill="#d0e8ff" stroke="#4a90e2" rx="4"/>
<text x="180" y="58" text-anchor="middle" font-size="11" fill="#333">n₂</text>
<text x="180" y="74" text-anchor="middle" font-size="10" fill="#666">[C,H,W]</text>
<!-- Ellipsis -->
<text x="230" y="68" font-size="20" fill="#999">···</text>
<rect x="270" y="35" width="60" height="60" fill="#d0e8ff" stroke="#4a90e2" rx="4"/>
<text x="300" y="58" text-anchor="middle" font-size="11" fill="#333">nₙ₋₁</text>
<text x="300" y="74" text-anchor="middle" font-size="10" fill="#666">[C,H,W]</text>
<!-- Dimension brace label -->
<line x1="10" y1="108" x2="330" y2="108" stroke="#999" stroke-width="1"/>
<text x="170" y="118" text-anchor="middle" font-size="11" fill="#666">← N nodes →</text>
</svg>
Approach 3: HTML details/summary for Collapsible Diagrams
Long diagrams interrupt reading flow. Use <details> / <summary> to make
them opt-in:
<details>
<summary><strong>Expand: GraphBatch batching diagram</strong></summary>
<pre>
GraphBatch.from_graphs([g1, g2, g3])
g1: N1=4 nodes, E1=5 edges
g2: N2=3 nodes, E2=4 edges
g3: N3=5 nodes, E3=6 edges
Batched graph:
┌─────────────────────────────────────────┐
│ nodes [0..3] = g1 nodes [4..6] = g2 │
│ nodes [7..11] = g3 │
│ │
│ edge_index offsets applied: │
│ g2 edges: +4 (N1=4) │
│ g3 edges: +7 (N1+N2=7) │
└─────────────────────────────────────────┘
batch vector: [0,0,0,0, 1,1,1, 2,2,2,2,2]
g1 g2 g3
</pre>
</details>
This keeps the article readable while letting interested readers expand the
detail.
Approach 4: Shape-Annotated Code Blocks
The simplest diagram is a well-annotated code block. Shape comments in line
with construction code are searchable, copyable, and work in every renderer:
import torch
from tgraphx import Graph
N, E, C, H, W = 32, 80, 3, 16, 16
node_features = torch.randn(N, C, H, W) # [32, 3, 16, 16]
edge_index = torch.randint(0, N, (2, E)) # [2, 80]
edge_features = torch.randn(E, 64) # [80, 64]
node_labels = torch.randint(0, 5, (N,)) # [32]
g = Graph(
node_features=node_features, # [N, C, H, W]
edge_index=edge_index, # [2, E]
edge_features=edge_features, # [E, d_e]
node_labels=node_labels, # [N]
)
Readers copy this code and immediately have correct shapes to work from.
Accessibility Considerations
For SVG diagrams:
- Always include aria-label or <title> inside the SVG
- Use role="img" for decorative diagrams, role="figure" for informational
- Ensure text labels are actual <text> elements, not rasterized
For ASCII diagrams:
- Wrap in <pre> or fenced code blocks for correct monospace rendering
- Consider a short prose summary immediately before the diagram
For details/summary:
- The <summary> text should describe what's inside, not just say "expand"
- Avoid nesting multiple levels of details/summary — it's confusing
When to Use Each Approach
| Situation | Recommended approach |
|---|---|
| Markdown-only environment (GitHub) | ASCII with code block |
| HTML static site (Hugo, Jekyll) | SVG inline + details/summary |
| Complex data flow diagram | SVG with labeled arrows |
| Quick shape reference | Shape-annotated code block |
| Long appendix-style diagram | details/summary wrapping ASCII or SVG |
| Print/PDF export needed | SVG (renders well in PDF) |
A Complete Example: Graph Construction Explainer
Combining approaches, a section explaining TGraphX's Graph might look like:
The Graph class validates shapes and devices at construction time.
g = Graph(
node_features=feats, # [N, d] — N nodes, d features each
edge_index=edges, # [2, E] — source/target pairs
edge_features=e_feats, # [E, de]
node_labels=labels, # [N]
)
What happens if shapes don't match?
If `edge_features.shape[0]` does not equal `edge_index.shape[1]`, you get an error at construction — before any forward pass runs. This prevents silent shape mismatches from propagating through training.This pattern — code block first, expandable detail for edge cases — keeps the
main reading flow clean while providing depth for readers who want it.