Performance & Rendering Strategy
How Plotcn delivers simple, fast default paths with SVG-first rendering, modular D3 engines, lazy Google Chart runtimes, narrow client islands, and an incremental large-data architecture.
Governing principle
Plotcn is designed so that the default path remains simple and fast, while the architecture can scale toward dense and computationally expensive visualizations later without forcing every V1 chart to carry that complexity.
The governing principle is:
Architectural rendering hierarchy
Plotcn supports three rendering strategies over time:
| Renderer | Primary role | Recommended scope |
|---|---|---|
| SVG | Default renderer for standard interactive charts, dashboards, and moderate data sizes | V1 Default |
| Canvas | Dense scatter, dense time-series, heatmaps, high-frequency updates, and very large mark counts | Later / Selective |
| WebGL | Extremely large datasets (100k+), GPU-oriented point clouds, advanced spatial/scientific workloads | Long-term specialization |
| Google Runtime | Host-executed, engine-owned enterprise charts (GeoChart, OrgChart, Timelines) | Dedicated singleton loader |
The architectural decision flow:
SVG as the primary default
SVG remains the primary Plotcn renderer for:
- Line & Area charts (single and multi-series)
- Bar & Column charts (grouped and stacked)
- Pie & Donut charts
- Radar & Radial charts
- Scatter plots with moderate mark counts (< 1,000 points)
- Composed dashboard charts
- Hierarchy and tree views
- Most financial & analytical dashboard cards
Why SVG aligns with source-first ownership
SVG aligns especially well with Plotcn's source-first philosophy because the installed component remains concise, readable, and directly editable by developers.
Mark count discipline
SVG should not be treated as infinitely scalable. Performance degrades rapidly when rendering:
- Thousands of individual
<circle>or<rect>nodes - Thousands of distinct path segments
- Deeply nested node-link graphs
- Massive heatmaps with un-virtualized DOM cells
- Excessive static annotations and tick labels
One path vs. many marks
For time-series visualizations, always prefer one continuous SVG path over thousands of individual segment elements:
Rather than rendering element-per-segment, generating a single bezier or linear path element reduces DOM node count from thousands down to one.
Point marker discipline
Do not render a marker for every point by default on dense lines. A line with 2,000 observations does not need 2,000 circles:
- Default: Clean SVG line path only.
- On hover: Active point cursor and crosshair marker.
- Optional: Sparse milestone markers at significant intervals.
Next.js server-first rendering model
Plotcn's website and consumer integrations remain primarily server-rendered.
Gallery rendering & lifecycle strategy
The /charts catalog and showcase galleries must remain lightweight. Cards entering the viewport are loaded progressively:
Stopping offscreen simulations
Never run active D3 force simulations across multiple gallery cards simultaneously:
- Static previews: For
/chartscards, show a settled, precalculated layout snapshot. - Viewport activation: Simulations start only when the card scrolls near the viewport.
- Timed settling: Simulations run briefly (e.g., 300 ticks) and then call
simulation.stop(). - Document visibility: When the browser tab is hidden, background simulations and animations pause automatically.
D3 module discipline
Plotcn strictly enforces focused, modular D3 imports. Never import the entire monolithic d3 package:
Each registry component item declares only the specific sub-modules it requires (d3-scale, d3-shape, d3-array, d3-force, etc.), preserving tree-shaking and preventing cross-engine dependency leakage.
Shared Google Charts runtime
Google Charts relies on an external runtime script. Plotcn wraps Google Charts in a singleton deduplicated loader:
- Single script tag: Multiple Google charts on the same page share one loader instance.
- Targeted package loading: Only requested packages (
corechart,geochart,timeline) are loaded on demand. - Decoupled draw cycle: Redraws on resize or prop updates occur in place without destroying and recreating the internal Google chart DOM.
- Unmount cleanup: Listeners and chart instances are disposed only when the React component unmounts.
State isolation and memoization
Memoization should be applied with evidence, not blanketed across every component.
Good candidates for memoization
- Heavy D3 domain calculations and scales (
useMemo) - Hierarchy and pack tree layouts
- Complex path generator calculations
- Spatial index constructions
Bad candidates for memoization
- Tiny arrays (< 20 elements)
- Static label strings or className strings
- Simple click handlers without downstream dependencies
Tooltip state isolation
Pointer motion fires up to 120 times per second. Updating tooltip state must never trigger geometry recalculation:
By decoupling tooltip coordinates and active datum state from the primary geometry pipeline, interaction remains buttery smooth at 60+ FPS.
Container-driven responsiveness
Plotcn charts are responsive to their parent container, not the global browser window:
ResizeObserver: Measures container width and height directly.requestAnimationFrameScheduling: Sub-pixel noise and rapid resizes are coalesced to redraw once per frame.- Zero-Dimension Protection: If a chart is mounted inside a collapsed tab or hidden drawer, rendering pauses until non-zero dimensions are reported.
- Layout Stability: Parent shells reserve minimum aspect ratios or heights to prevent Cumulative Layout Shift (CLS).
Resource cleanup invariants
Every chart with subscriptions must clean up its resources on unmount:
ResizeObserver.disconnect()- Window and document event listeners removed
- Active
requestAnimationFrameIDs cancelled - D3 force simulations stopped (
simulation.stop()) - Google Chart event listeners cleared (
google.visualization.events.removeAllListeners(chart)) - Web Workers terminated if spawned
[!CAUTION] Uncollected listeners, running simulations, or uncancelled animation frames in single-page applications cause memory leaks that degrade browser performance over time.
Large-data roadmap
While V1 prioritizes SVG-first clarity, Plotcn is architected to scale smoothly as data density increases:
Future large-data capabilities
- LTTB Downsampling: Largest-Triangle-Three-Buckets downsampling for dense time-series (e.g., reducing 50,000 telemetry samples to 1,200 rendered points while preserving visual extrema).
- Canvas Renderers: Targeted Canvas components for high-frequency financial tickers and correlation matrices.
- Viewport Windowing: Rendering only the visible domain during pan and zoom interactions, skipping marks outside the viewport.
- Web Workers: Offloading heavy layout computation, GeoJSON parsing, and statistical binning off the main browser thread.
The 20 Architectural Performance Invariants
Plotcn enforces twenty non-negotiable performance invariants:
- SVG is the default renderer for all standard visualizations.
- Canvas is introduced only when density or frame-rate requirements justify it.
- WebGL is a specialized long-term path, not a V1 requirement.
- Documentation remains server-first with Server Components.
- Interactive charts remain narrow client islands.
- Gallery previews do not initialize heavy offscreen work.
- D3 imports remain modular (
d3-scale,d3-shape, not* as d3). - Google runtime is lazy, deduplicated, and shared.
- Tooltip movement never recomputes chart geometry.
- Theme switching does not recompute geometry when only color tokens change.
- Resize handling is container-driven and scheduled with
requestAnimationFrame. - Expensive transforms run once per meaningful data or prop change.
- Memoization is evidence-driven, never blanket applied.
- Large-data downsampling remains separate from the original source data.
- Canvas and WebGL never weaken accessibility contracts (text summaries and data tables remain).
- Force simulations stop when settled or unmounted.
- All observers, listeners, and animation frames are cleaned up on unmount.
- A 100+ component catalog does not imply 100+ eager preview bundles.
- Detail page prop preview labs can mount lazily.
- Performance optimization must preserve source readability for developers who install the code.