Schema Editor
No Framework, No Problem: Building a High-Performance CAD Engine with Vanilla JS
TLDR
Interactive CAD applications (handling thousands of SVG elements, real-time Manhattan wire routing, and $O(\log N)$ spatial queries) suffer severe frame drops when built on reactive virtual DOM frameworks (React, Vue). The GINEXYS Schema Editor achieves a stable 60fps performance profile under 100KB gzipped by utilizing zero dependencies: direct SVG DOM attribute mutations, KD-tree spatial indexing, and a 4-phase geometry pipeline.| Architecture Choice | Virtual DOM Frameworks | Pure Vanilla JS CAD Engine |
|---|---|---|
| State Reconciliation | VDOM diffing overhead per frame | Direct SVG DOM attribute mutation (x, y, d) |
| Spatial Hit Testing | $O(N)$ full array iterations | $O(\log N)$ KD-Tree spatial index queries |
| Bundle Footprint | 200KB to 500KB framework overhead | < 100KB Gzipped (Zero dependencies) |
Problem statement: the VDOM re-render overhead in CAD applications
Modern frontend web frameworks excel at standard CRUD user interfaces. However, they impose a severe performance penalty on interactive CAD engines.
In a diagram editor, dragging a component requires:
- Updating element position transforms.
- Recalculating connected orthogonal wire paths in real time.
- Updating selection handles and alignment guide overlays.
Technical failure mode: $O(N)$ hit testing in large schematics
When a user moves their mouse over a canvas containing 2,000 components and wires, determining which element or pin lies beneath the cursor via array iteration ($O(N)$) freezes the UI thread.
In a framework environment where hit tests trigger reactive state changes, every mouse move stalls the main render loop.
The fix & architecture: direct mutations, KD-trees & geometry pipelines
1. Direct SVG DOM attribute mutation
During active mouse drag operations, update target SVG element attributes (x, y, d) directly inside the mousemove event handler, bypassing reactive state loops:
// Direct DOM mutation for 60fps dragging
function onDragMove(e) {
const worldPos = screenToWorld(e.clientX, e.clientY);
targetSvgElement.setAttribute('transform', translate(${worldPos.x}, ${worldPos.y}));
updateConnectedWiresDirectly(targetSvgElement, worldPos);
}
2. $O(\log N)$ KD-tree spatial indexing
To make spatial queries instantaneous, build a KD-tree index over all canvas element bounding boxes, reducing hit-test queries from $O(N)$ to $O(\log N)$:// Spatial Query using KD-Tree Index
const candidateElements = spatialKdTree.queryPoint(worldCursorPos, hitRadius);
const hoveredElement = findExactHit(candidateElements, worldCursorPos);
3. 4-Phase geometry pipeline
Separate canvas calculations into discrete, non-DOM phases before applying changes:- Raw Input Capture: Screen coordinates to world space transformation.
- Snap & Constraint Pass: Axis-independent snapping and grid alignment.
- Routing & Layout Pass: Orthogonal Manhattan pathfinding calculation.
- DOM Paint Pass: Direct SVG attribute application.
Rule of thumb: Eliminate virtual DOM reconciliation when building high-density canvas editors. Mutate SVG attributes directly during interactive drag events and use $O(\log N)$ spatial indexing for hit testing.
Read this post in the full Engineering Journal →