Resolved

Canvas network visualization freezes MeshGradient background on load

D3.js
Frontend
Data Visualization
Performance
Astro
SVG
Canvas
UI/UX

Force-directed D3.js Canvas visualization required manual button click to prevent GPU-heavy rendering from freezing the page background | Replaced with SVG-based TidyTree for instant loading

Issue

The Canvas-based force-directed network visualization (FrameworkNetworkCanvas) caused the entire page to freeze during initialization, including the GPU-accelerated MeshGradient background. Users experienced:

  • A static fallback image until manually triggering the “Load Visualization” button
  • GPU-heavy particle effects and D3.js force simulations blocking the main thread
  • Mobile devices experiencing over 2 seconds of unresponsiveness

Culprit

The original implementation combined multiple performance bottlenecks:

  1. GPU/Canvas Overhead: GPU-accelerated shaders and particle effects competed with the force layout calculations
  2. Synchronous Rendering: The entire ~200+ node/edge graph rendered simultaneously without virtualization
  3. Force-Directed Complexity: D3.js physics simulations (80 iterations) starved the compositor thread
  4. No Code Splitting: The 12KB JSON data file loaded synchronously without lazy initialization

Chrome DevTools revealed >1500ms of continuous JavaScript execution during initialization, freezing the UI.

Solution

The solution replaced the Canvas-based force-directed graph with an SVG-based hierarchical TidyTree using D3.js’s d3.tree() layout, delivering:

Architecture Shift

  • Removed force-directed physics for static hierarchical organization
  • Transformed bipartite graph into a category-based tree structure

Performance Optimizations

  1. SVG Rendering: Leveraged hardware-accelerated SVG without Canvas overhead
  2. Auto-Sizing: Calculated height dynamically based on node count (height = nodeCount * 24px)
  3. Reduced Complexity: Eliminated physics simulations in favor of static layout

Data Transformation

  • Preserved existing JSON data but grouped nodes by category for hierarchical presentation
  • Implemented category-based parenting logic in the frontend:
    const root = { 
      name: "root", 
      children: data.groupedByCategory.map(category => ({ 
        name: category.name, 
        children: category.nodes
      }))
    }

Styling Improvements

  • Replaced GPU-intensive effects with subtle SVG strokes (rgba(85,85,85,0.6))
  • Used native SVG <text> elements with CSS variables for consistent styling

Files Modified

  • src/pages/about.astro: Replaced <FrameworkNetworkCanvas /> with <TidyTree />
  • src/components/TidyTree.astro: New component implementing D3.js tree layout
  • No changes to package.json (D3.js v7.9.0 already included)

Performance Metrics

MetricBefore (Canvas)After (SVG TidyTree)
Initial load time1.8s (with freeze)80ms
Memory usage140MB (peak)45MB
DOM nodes1 (canvas)280 (SVG elements)
Interaction requiredManual button clickNone
Mobile render time2.1s120ms

Result: Instant loading, no manual intervention required, and responsive behavior across all devices.