- Implementation plan for Vue-based node rendering system - Migration strategy from canvas to Vue components - Widget system integration documentation
15 KiB
Vue Node Migration Plan
Executive Summary
This plan outlines a phased migration from LiteGraph canvas rendering to Vue-based DOM rendering for ComfyUI nodes. Vue-based nodes allows access to component libraries (PrimeVue) and CSS frameworks, which increases iteration speed significantly. The migration preserves LiteGraph as the source of truth for graph logic while leveraging Vue components for rich, accessible node interfaces. Increased development speed will facilitate next gen UI/UX changes: .
Goals and Objectives
Primary Goals
- Enable rapid UI iteration with Vue's component model, PrimeVue, and CSS frameworks
- Maintain performance for workflows with 100+ nodes
- Preserve extension compatibility (90%+ without changes)
- Improve developer experience
Success Metrics
- New components implementable in <1 hour (vs. current DOM manipulation)
- Performance regression <25% for 100-node workflows
- 90% of existing extensions work unmodified
- 3x faster UI development iteration
- Memory usage within 1.5x of canvas-only approach
Non-Goals
- Complete canvas replacement (connections remain canvas-rendered)
- Mobile/touch optimization (separate initiative)
- Workflow format changes (must remain compatible)
- Extension API redesign (compatibility layer only)
Definition of Done
- All core node types render via Vue components
- Canvas handles only connections and viewport
- Performance benchmarks meet targets
- Extension compatibility layer tested with top 20 extensions
- Migration guide published for extension developers whose extensions are broken by the migration
- Feature flag allows instant rollback
Architecture Overview
Current State
LiteGraph with mixed rendering approaches:
- Canvas: node bodies, connections, grid, selection
- Widgets: Three types coexist:
- Canvas widgets (drawn in 2D context)
- DOM widgets (manually positioned HTML elements)
- Vue widgets (components positioned via DOM)
- Events: Canvas handles most interactions, delegates to widgets
- State: Stored in LiteGraph node/graph objects
Target State
Hybrid rendering with clear separation:
- Canvas: connections, grid, viewport pan/zoom, selection rectangles
- DOM (Vue): all node contents and widgets unified as Vue components
- Transform Pane: single Vue-managed container synchronized with canvas transforms
- State: LiteGraph remains source of truth, Vue observes changes
Hybrid Approach
During migration, both systems coexist:
- Feature flag controls Vue rendering per node type
- Canvas nodes and Vue nodes can connect normally
- Shared event system handles both rendering modes
- Progressive migration allows testing at each phase
Technical Design
Component Architecture
GraphCanvas.vue
└── TransformPane.vue (synchronized with canvas transforms)
└── LGraphNode.vue (v-for visible nodes)
├── NodeHeader.vue (title, controls)
├── NodeSlots.vue (input/output connection points)
│ ├── InputSlot.vue (connection target)
│ └── OutputSlot.vue (connection source)
├── NodeWidgets.vue (parameter controls)
│ └── [Widget components rendered here]
│ ├── NumberWidget.vue
│ ├── StringWidget.vue
│ ├── ComboWidget.vue
│ └── [etc...]
└── NodeContent.vue (custom content area)
Slots = connection points for node edges Widgets = UI controls for node parameters
State Management
- One-way data flow: LiteGraph → Vue components (props down, events up)
- Widget values flow from LiteGraph to Vue as props
- User interactions emit events that update LiteGraph
- Updated values flow back to Vue, completing the cycle
- LiteGraph as source of truth: All node/graph state remains in LiteGraph
- Vue as view layer: Components observe and reflect LiteGraph state
Event System
- Canvas events: Pan, zoom, connection dragging, box selection
- DOM events: Node interactions, widget inputs, context menus
- Transform sync: No coordinate mapping needed - transforms handle positioning
- (Future) Event delegation: Single listener on TransformPane for efficiency
- (Future) Touch handling: Unified pointer events for mouse/touch consistency
Positioning Strategy: CSS Transforms
For positioning nodes in the DOM, we'll use CSS transforms rather than absolute positioning with top/left. This decision is based on significant performance benefits validated by industry leaders (React Flow, Excalidraw, tldraw, Figma).
Core Implementation
<!-- TransformPane synchronized with canvas -->
<div class="transform-pane" :style="{ transform: `scale(${zoom}) translate(${panX}px, ${panY}px)` }">
<!-- Individual nodes use simple translate -->
<div v-for="node in visibleNodes"
:style="{ transform: `translate(${node.x}px, ${node.y}px)` }">
</div>
Key Optimizations
- CSS Containment:
contain: layout style paintisolates node rendering - GPU Acceleration:
will-change: transformduring interactions only - Batched Updates: CSS custom properties for efficient updates
Handling PrimeVue Overlays
Portal strategy for components with fixed positioning:
<Teleport to="body" v-if="showOverlay">
<Popover :style="{ position: 'fixed', left: `${coords.x}px`, top: `${coords.y}px` }">
</Teleport>
Alternative Approaches
| Approach | Performance | Complexity | Use Case |
|---|---|---|---|
| CSS Transforms | Excellent (GPU) | Medium | ✅ Our choice |
| Absolute Position | Poor (reflow) | Low | Small node counts |
| Canvas Rendering | Best | High | Not compatible with Vue |
| SVG | Good | Medium | Better for connections |
Migration Strategy
Phase 1: Widget Migration
For each widget:
- Create a new Vue component for the widget, using the API defined here: https://www.notion.so/drip-art/Widget-Componet-APIs-2126d73d365080b0bf30f241c09dd756
- If the widget existed before, alias the constructor to the new component (q: why not just replace entirely? any reason to keep the old constructor?)
- If the widget is new, create a new constructor for the widget and add to widgets.ts
- Implement the existing widget interface in the new component (i.e., create Vue-compatible mappings of the LG widget's props and events)
- Avoid components that use things like fixed positioning, teleport,
fill-available, in the widget component (e.g., PrimeVue's Popover, Tooltip, Select) as they will require a portal strategy to work with transforms
Phase 2: Node Migration
- Create a new Vue component for the node
- [maybe later] Create conditional render for LOD, distance cull, and viewport cull
- Implement the existing node interface in the new node component (i.e., create Vue-compatible mappings of the LG node's props and events)
Phase 3: Transform Pane
- Create the transform pane
- Synchronize the transform pane with the canvas transforms
- Use
transform-originto position the transform pane in accordance with the canvas - Use
will-change: transformand verify with DevTools that nodes are on a single layer and not being promoted- NOTE: in future, we need to actively prevent layer promotion (see promotion conditions: https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/platform/graphics/compositing_reasons.h;l=18;drc=4e8e81f6eeb6969973f3ec97132d80339b92d227)
Phase 4: Interaction System
- Map all existing events from previous interface
- Map all lifecycle hooks
- Add event delegation for transform pane events
- Restrict touch events to the transform pane
- For any event (except those affecting the entire transform pane) that affects compositing (e.g., moving nodes, resizing nodes), batch in RAF
Phase 5: Portals
- Create a portal component that can be used to render components that use fixed positioning, teleport,
fill-available, etc.
Phase 6: Performance Optimizations
- Create baseline performance metrics
- For each optimization, test and iterate while comparing against baseline:
- Implement viewport culling or LOD
- It may be less efficient to cull nodes if we are only doing compositing and not actually recalc and reflow. This must be manually verified. In any case, there is still probably a very large threshold to implement viewport culling by, but it may be the case that it's not worth the effort to do.
- Implement distance culling and LOD
- (optional) Implement virtualization, prefetching, prerendering, preloading, preconnecting if still necessary
- Implement viewport culling or LOD
Phase 7: Extension Migration
- Review all of the most common touch points from extension space
- Determine if any compatibility mappings are still needed, then implement them and add to public API
- Gradually test with more and more frontend extensions
Performance
Benchmarking Strategy
- Use existing performance wrapper playwright testing strategy created in https://www.notion.so/drip-art/Analyze-Performance-Impact-of-only-using-Vue-widgets-20b6d73d36508080a14cea0b8dce7073?source=copy_link#20d6d73d365080409a8ccc68f501284e
- Or, a subset of it
Optimization Techniques
- Use
will-change: transformandtransform: translateZ(0)to force GPU acceleration - Use
contain: layout style paintto tell the browser this element won't affect outside layout - Use
transform-originto position the transform pane in accordance with the canvas Usetransform: translate3d(round(var(--x), 1px), round(var(--y), 1px), 0)to snap to pixels during non-animated states
Scaling Targets
- 256 nodes full LOD
- 1000 nodes culled
Production Monitoring
For desktop users who have consented to telemetry:
- Mixpanel Events: Track migration feature adoption and performance metrics
- Node rendering time percentiles (p50, p90, p99)
- Frame rate during node interactions
- Memory usage with different node counts
- Sentry Performance: Monitor real-world performance regressions
- Transaction traces for node operations
- Custom performance marks for render phases
- Error rates specific to Vue node rendering
Extension Compatibility Plan
Migration Guide
Comprehensive migration documentation will be published at https://docs.comfy.org including:
- Step-by-step migration instructions for common patterns
- Code examples for converting canvas widgets to Vue components
- API compatibility reference
- Performance optimization guidelines
Compatibility Layer
[Placeholder: Supporting both rendering modes]
Deprecation Timeline
[Placeholder: List things that will be deprecated fully]
Developer Communication
- Email notifications via existing developer mailing list
- Discord announcements in dedicated devrel channel
- Automated PRs to affected repositories (if breaking changes required)
Testing
Testing Strategy
Component Tests
- Create component tests for each widget and node
Integration Tests
- Create integration tests for canvas and transform pane synchronization
Performance Tests
maybe
Migration of Existing Test Suites
Unit Tests
[Placeholder: strategy for migrating unit tests]
Browser Tests
[Placeholder: strategy for migrating browser tests]
Risk Mitigation
Technical Risks
Performance Degradation
- Risk: DOM nodes significantly slower than canvas rendering
- Mitigation: Aggressive viewport culling, CSS containment, GPU acceleration
- Monitoring: Automated performance benchmarks on each PR
Memory Usage
- Risk: 1000+ DOM nodes consume excessive memory
- Mitigation: Component pooling, virtualization for large workflows
- Detection: Memory profiling in browser tests
Extension Breaking Changes
- Risk: Popular extensions stop working
- Mitigation: Compatibility layer maintaining critical APIs
- Testing: Top 20 extensions tested before each phase
State Synchronization Bugs
- Risk: LiteGraph and Vue state diverge
- Mitigation: Strict one-way data flow, comprehensive event testing
- Prevention: State invariant checks in development mode
Rollback Plan
- Feature flag:
enable_vue_nodessetting (default: false) - Gradual rollout: Enable for specific node types first
- Quick revert: Single flag disables all Vue rendering
- Data compatibility: No changes to workflow format ensures backward compatibility
Timeline and Milestones
Week 1
[Placeholder: Initial milestones]
Week 2
[Placeholder: Mid-term milestones]
Week 3
[Placeholder: Final milestones]
Open Questions
Widget Constructor Aliasing
- Why keep old constructors vs. full replacement? (Phase 1, line 53)
- Is this for backwards compatibility with existing extensions?
Viewport Culling Efficiency
- At what node count does viewport culling become beneficial? (Phase 6, line 89)
- Does the compositing-only benefit outweigh the Vue mount/unmount cost?
Extension Compatibility
- How much of extension surface area to attempt to cover in compatibility layer?
- Which extension APIs are most critical to preserve?
Transform Pane Synchronization
- How to handle canvas zoom/pan events?
- Should transform sync be RAF-batched or immediate?
Event Delegation
- Which events stay on canvas vs. move to DOM?
- Option A: Canvas handles all drag/pan, DOM handles clicks/inputs
- Option B: DOM handles everything except connection dragging
- Note: This option provides the best UX - users expect DOM elements to be fully interactive while keeping complex connection logic in canvas
- Option C: Context-aware delegation based on interaction type
LOD (Level of Detail) System
- What defines each LOD level?
- High: Full widgets and styling
- Medium: Simplified widgets, reduced effects?
- Low: Title and connections only?
- Transition triggers: zoom level, node count, or performance metrics?
- Note: Zoom level as primary trigger is most predictable for users, with node count as override for performance protection
Interaction System Migration
- How to maintain gesture consistency between canvas and DOM nodes?
- Multi-select behavior across rendering boundaries?
Appendices
A. Prototype Learnings
Detailed findings and discoveries from process of developing Vue widgets and Vue nodes prototype (in vue-node-test branch)
- Constructing components easy, difficulty is performance and compatibility
B. Performance and Browser Rendering
- https://www.notion.so/drip-art/Analyze-Performance-Impact-of-only-using-Vue-widgets-20b6d73d36508080a14cea0b8dce7073?source=copy_link#20d6d73d365080409a8ccc68f501284e
- https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/platform/graphics/compositing_reasons.h;l=18;drc=4e8e81f6eeb6969973f3ec97132d80339b92d227
- https://webperf.tips/tip/browser-rendering-pipeline/
- https://webperf.tips/tip/layers-and-compositing/
- https://webperf.tips/tip/layout-thrashing/