Litegraph.js

repository·master·Indexed 42 days ago

https://github.com/comfy-org/litegraph.js

A TypeScript library for creating node-based graphs in the browser and NodeJS, similar to Unreal Engine's Blueprints. It provides a graph node editor using HTML5 Canvas and is heavily used by ComfyUI for workflow orchestration. Features include custom node creation via LGraphNode, a CanvasPointer API for interaction handling, and a LinkConnector class for managing graph connections. Note: This repository is archived; source code has migrated to the ComfyUI Frontend repository.

Tokens
15.3K
Snippets
39
Records
53
Agent score
54%

What's inside Litegraph.js

  1. Important: Repository Archival and Migration

    master

    ⛔ ARCHIVED

    This repository is archived and no longer maintained. The code has been integrated directly into the ComfyUI Frontend repository.

    🚀 New Location

    As of August 5, 2025, the source code is located within the ComfyUI Frontend monorepo:

    • Source Code: https://github.com/Comfy-Org/ComfyUI_frontend/tree/main/src/lib/litegraph
    • Repository: https://github.com/Comfy-Org/ComfyUI_frontend

    🔄 Migration for Developers

    If you are working within the ComfyUI codebase, the import path has changed:

    • Old path: @comfyorg/litegraph
    • New path: @/lib/litegraph

    All issues and contributions should now be directed to the ComfyUI Frontend repository.

  2. Enable TypeScript typing for Litegraph widgets

    master

    To get in-IDE typing (like VS Code autocomplete) for Litegraph widgets in a JavaScript project, you can use JSDoc to import the types from the @comfyorg/litegraph package.

    /** @import { IWidget } from './path/to/@comfyorg/litegraph/litegraph.d.ts' */
    /** @type {IWidget} */
    const widget = node.widgets[0]
    widget.onPointerDown = function (pointer, node, canvas) {}
  3. How to code a new Node type

    master

    To create a custom node, extend the LGraphNode class. You must define the node's title, set up inputs and outputs in the constructor, and implement the onExecute method to define the node's logic. Finally, register the node type using LiteGraph.registerNodeType.

    import { LiteGraph, LGraphNode } from "./litegraph"
    
    class MyAddNode extends LGraphNode {
      // Name to show
      title = "Sum"
    
      constructor() {
        this.addInput("A", "number")
        this.addInput("B", "number")
        this.addOutput("A+B", "number")
        this.properties.precision = 1
      }
    
      // Function to call when the node is executed
      onExecute() {
        var A = this.getInputData(0)
        if (A === undefined) A = 0
        var B = this.getInputData(1)
        if (B === undefined) B = 0
        this.setOutputData(0, A + B)
      }
    }
    
    // Register the node type
    LiteGraph.registerNodeType("basic/sum", MyAddNode)
  4. Understand ExecutableNodeDTO and graph compilation

    master

    When compiling a graph for execution, especially one containing subgraphs, ExecutableNodeDTO is used to represent nodes in a flattened, executable format. It wraps an underlying LGraphNode or SubgraphNode and provides a way to resolve links across subgraph boundaries.

    Key characteristics:

    • Execution ID: Every node has a unique id representing its path through nested subgraphs (e.g., 1:2:3, where 1 and 2 are subgraph instance IDs and 3 is the actual node ID).
    • Flattened View: The DTO provides a view where subgraph instances are expanded into their constituent nodes, allowing the execution engine to treat the entire structure as a single continuous network.
    • Link Resolution: It includes methods to resolve where an input or output actually originates, handling bypass modes, virtual nodes, and subgraph I/O nodes.
  5. How CanvasPointer handles click and drag interactions

    master

    CanvasPointer is a utility class designed to manage pointer interactions (mouse, touch, etc.) on a canvas element. It abstracts raw pointerdown, pointermove, and pointerup events into high-level semantic actions like clicks, double-clicks, and drags.

    Interaction Lifecycle

    Depending on the user's movement and timing, CanvasPointer will trigger one of the following sets of callbacks:

    • Click: Triggered if the pointer is released near the original pointerdown position within the bufferTime.
    • Double-Click: Triggered if a click occurs within doubleClickTime of the previous successful click.
    • Drag: Triggered if the pointer moves beyond the maxClickDrift threshold or stays down longer than bufferTime.

    Key Properties and Configuration

    • maxClickDrift: The maximum distance (in pixels) the pointer can move from the initial pointerdown position before the interaction is considered a drag instead of a click. Defaults to 6.
    • bufferTime: The maximum time (in milliseconds) to ignore click drift. Defaults to 150.
    • doubleClickTime: The maximum gap (in milliseconds) between pointerup and pointerdown to be considered a double-click. Defaults to 300.
    • clearEventsOnReset: If true (default), calling reset() clears the eDown, eMove, and eUp event references.

    Usage Pattern

    To use CanvasPointer, you typically instantiate it with a target Element, then hook its down, move, and up methods into your event listeners. You then define the desired behavior using the semantic callback properties.

    const pointer = new CanvasPointer(canvasElement);
    
    // Define semantic behaviors
    pointer.onClick = (e) => console.log('Clicked at:', e.clientX, e.clientY);
    pointer.onDrag = (e) => console.log('Dragging...');
    pointer.onDragEnd = (e) => console.log('Drag finished');
    
    // Hook into raw events
    canvasElement.addEventListener('pointerdown', (e) => pointer.down(e as CanvasPointerEvent));
    canvasElement.addEventListener('pointermove', (e) => pointer.move(e as CanvasPointerEvent));
    canvasElement.addEventListener('pointerup', (e) => pointer.up(e as CanvasPointerEvent));
  6. Understand the SerialisableGraph schema

    master

    The SerialisableGraph interface defines the structure of a serialized graph workflow. It extends BaseExportedGraph and includes versioning, state, and collections of graph elements. This is the primary format used for modern graph exports.

    Key fields:

    • version: Schema version (currently 0 | 1).
    • state: The LGraphState of the graph.
    • nodes: An array of ISerialisedNode objects.
    • links: An array of SerialisableLLink objects.
    • reroutes: An array of SerialisableReroute objects.
    • groups: An array of ISerialisedGroup objects.
    • extra: LGraphExtra data.
    export interface SerialisableGraph extends BaseExportedGraph {
      version: 0 | 1
      state: LGraphState
      groups?: ISerialisedGroup[]
      nodes?: ISerialisedNode[]
      links?: SerialisableLLink[]
      floatingLinks?: SerialisableLLink[]
      reroutes?: SerialisableReroute[]
      extra?: LGraphExtra
    }
  7. Manage link connections with LinkConnector

    master

    The LinkConnector class is a core component of the Litegraph canvas responsible for handling the lifecycle of link connections and movements. It manages the state of dragging new links, moving existing links, and handling drop events on nodes, reroutes, or subgraphs.

    Key Capabilities:

    • Connecting New Links: Dragging from an output slot to an input slot (or vice versa).
    • Moving Existing Links: Dragging an existing link or a reroute to a new connection point.
    • Subgraph Support: Specialized handling for connecting links to and from SubgraphInputNode and SubgraphOutputNode.
    • Reroute Management: Dragging links from or to Reroute objects.
    • Event System: Dispatches events like before-move-input, before-drop-links, and reset to allow extensions to intercept or cancel operations.

    State Management

    The state property (of type LinkConnectorState) tracks the current operation:

    • connectingTo: Either 'input', 'output', or undefined (no operation).
    • multi: Indicates if multiple links are being moved simultaneously.
    • draggingExistingLinks: Indicates if existing links are being repositioned rather than new ones being created.
    • snapLinksPos: Optional coordinates for snapping connecting links.
    import { LinkConnector } from "./LinkConnector";
    
    // Example of how a LinkConnector might be initialized (internal to LGraphCanvas)
    const connector = new LinkConnector((connectingLinks) => {
      // Callback for legacy link updates
    });
    
    // Checking connection state
    if (connector.isConnecting) {
      console.log("Currently dragging a link to:", connector.state.connectingTo);
    }
  8. How LLink and ResolvedConnection work together

    master

    In LiteGraph, links are stored as lightweight objects containing only primitive IDs (NodeId, number) to keep the graph structure small and easy to serialize.

    To perform logic on the actual objects (like checking a slot's value or a node's type), you must "resolve" the link. The ResolvedConnection type is the result of this process. It acts as a union type that ensures you can safely access either standard node slots OR subgraph I/O slots, but not both simultaneously in a way that would be invalid.

    When you resolve a link, the system looks up the origin_id and target_id in the provided network to find the corresponding LGraphNode instances.

  9. How positionable items work in LiteGraph

    master

    A Positionable object is an entity that can be placed, selected, and moved within the graph coordinate system. It implements HasBoundingRect (providing a boundingRect for collision and rendering) and Parent (allowing it to contain child objects).

    Key capabilities include:

    • Movement: Use move(deltaX, deltaY, skipChildren?) to shift the item. Setting skipChildren to true prevents child objects (like group contents) from moving with the parent.
    • Grid Snapping: Use snapToGrid(snapTo) to align the item to a grid of a specific size. This returns true if the item successfully moved, or false if the movement was rejected (e.g., because the item is pinned).
    • Selection: Items can be marked as selected. You can hook into selection lifecycle events using onSelected() and onDeselected() callbacks.
    // Example of an object implementing Positionable
    const myItem: Positionable = {
      id: 123,
      pos: [100, 100],
      selected: false,
      pinned: false,
      removable: true,
      boundingRect: [100, 100, 50, 50],
      move(dx, dy, skipChildren) {
        // implementation
      },
      snapToGrid(snapTo) {
        // implementation
        return true;
      }
    };
  10. How nodes and slots are structured

    master

    In LiteGraph, nodes interact with the graph through Slots.

    • Slots (INodeSlot): These represent the connection points on a node. They have a type (defined by ISlotType, which can be a string or number), a name, and optional label or localized_name for UI display. Slots can be configured with dir (direction), shape, and color properties.
    • Input Slots (INodeInputSlot): An input slot includes a reference to the current link (if connected) and an optional widget (for widget-based inputs).
    • Output Slots (INodeOutputSlot): An output slot tracks a list of connected links and an optional slot_index.
    • Found Slots (IFoundSlot): When interacting with the UI, a slot is identified by its index, its type, and its link_pos (the center point of the rendered slot connection).
  11. Understand Canvas-extended Pointer and Mouse Events

    master

    When working with LGraphCanvas, standard browser events (like PointerEvent or MouseEvent) are extended with graph-specific spatial data. This allows you to access coordinates relative to the graph's coordinate system rather than just the HTML canvas element.

    Key extensions include:

    • canvasX / canvasY: The position of the event in graph space (not canvas space).
    • deltaX / deltaY: The movement delta of the pointer.
    • safeOffsetX / safeOffsetY: A workaround for Firefox, which may return 0 for standard offsetX/offsetY properties.

    Common extended event types include:

    • CanvasPointerEvent: A PointerEvent combined with CanvasMouseEvent extensions.
    • CanvasMouseEvent: A MouseEvent combined with CanvasPointerExtensions.
    • CanvasDragEvent: A DragEvent combined with CanvasPointerExtensions.