Files
2025-06-15 16:32:07 -07:00

8.4 KiB

Node Interface Reference

Complete Node API

This reference covers all attributes and methods available when creating custom nodes in ComfyUI.

Required Components

Class Definition

Every custom node must be a Python class. The class name will be used internally, while the display name can be customized.

class YourNodeClassName:
    """Docstring describing what your node does"""

INPUT_TYPES (Required)

A class method that returns a dictionary defining the node's inputs.

@classmethod
def INPUT_TYPES(cls):
    return {
        "required": {
            # Required inputs must be connected
            "input_name": ("TYPE", options_dict),
        },
        "optional": {
            # Optional inputs can be left unconnected
            "optional_name": ("TYPE", options_dict),
        },
        "hidden": {
            # Hidden inputs not shown in UI
            "unique_id": "UNIQUE_ID",
            "prompt": "PROMPT",
            "extra_pnginfo": "EXTRA_PNGINFO",
        }
    }

RETURN_TYPES (Required)

A tuple defining the types of outputs the node produces.

RETURN_TYPES = ("IMAGE", "MASK")

FUNCTION (Required)

The name of the method to call when the node executes.

FUNCTION = "process"

Execution Method (Required)

The method that performs the node's computation.

def process(self, **kwargs):
    # All inputs from INPUT_TYPES are passed as keyword arguments
    # Must return a tuple matching RETURN_TYPES
    return (output1, output2)

Optional Components

CATEGORY

Where the node appears in the add node menu. Use "/" for submenu organization.

CATEGORY = "image/filters"

RETURN_NAMES

Human-readable names for outputs (must match RETURN_TYPES length).

RETURN_NAMES = ("processed_image", "generated_mask")

OUTPUT_NODE

Set to True if this node produces final outputs (like Save Image).

OUTPUT_NODE = True

OUTPUT_TOOLTIPS

Tooltips for each output socket.

OUTPUT_TOOLTIPS = ("The processed image", "The generated mask")

DESCRIPTION

A description shown in the UI when hovering over the node.

DESCRIPTION = "Applies advanced filtering to images"

Input Type Options

Basic Input Options

All input types support these options:

{
    "default": value,           # Default value
    "forceInput": True,        # Force connection (no widget)
    "lazy": True,              # Enable lazy evaluation
    "tooltip": "Help text",    # Tooltip on hover
}

Numeric Inputs (INT, FLOAT)

"number": ("FLOAT", {
    "default": 1.0,
    "min": 0.0,
    "max": 10.0,
    "step": 0.01,
    "round": 0.001,           # Round to this precision
    "display": "slider",      # or "number"
})

String Inputs

"text": ("STRING", {
    "default": "Enter text",
    "multiline": True,        # Multi-line text box
    "dynamicPrompts": True,   # Enable dynamic prompts
    "placeholder": "Type here..."
})

Boolean Inputs

"enable": ("BOOLEAN", {
    "default": True,
    "label_on": "Enabled",    # Label when True
    "label_off": "Disabled"   # Label when False
})

Combo Box (Dropdown)

"mode": (["option1", "option2", "option3"], {
    "default": "option1"
})

Image Upload

"image": ("IMAGE", {
    "image_upload": True,     # Enable upload button
})

Advanced Features

IS_CHANGED Method

Control when the node is re-executed:

@classmethod
def IS_CHANGED(cls, **kwargs):
    # Return a value that changes when node should re-execute
    # Can return hash, timestamp, random value, etc.
    return float("nan")  # Always re-execute

VALIDATE_INPUTS Method

Custom validation before execution:

@classmethod
def VALIDATE_INPUTS(cls, **kwargs):
    # Return True if inputs are valid
    # Return error message string if invalid
    if kwargs.get("value", 0) < 0:
        return "Value must be non-negative"
    return True

Hidden Inputs

Access special system information:

"hidden": {
    "unique_id": "UNIQUE_ID",          # Node's unique ID
    "prompt": "PROMPT",                # Full workflow prompt
    "extra_pnginfo": "EXTRA_PNGINFO",  # PNG metadata dict
    "dynprompt": "DYNPROMPT"           # Dynamic prompt object
}

Lazy Evaluation

Enable on-demand execution:

"input": ("TYPE", {"lazy": True})

# In execution method:
def process(self, input):
    # Input might be a lazy object
    if hasattr(input, '__call__'):
        actual_value = input()  # Evaluate lazy input

Type System

Built-in Types

# Basic types
"STRING"              # Text strings
"INT"                 # Integers
"FLOAT"               # Floating point numbers
"BOOLEAN"             # True/False values

# Image types
"IMAGE"               # Image tensor [B,H,W,C]
"MASK"                # Mask tensor [H,W] or [B,H,W]
"LATENT"              # Latent representation

# Model types
"MODEL"               # Diffusion model
"CLIP"                # CLIP text encoder
"VAE"                 # Variational autoencoder
"CONDITIONING"        # Text conditioning
"CONTROL_NET"         # ControlNet model

# Advanced types
"SAMPLER"             # Sampling method
"SIGMAS"              # Sigma schedule
"GUIDER"              # Guidance model
"NOISE"               # Noise tensor

# Special types
"*"                   # ANY type (matches anything)
"STRING,FLOAT,INT"    # Union types

Custom Types

You can define custom types:

class MY_CUSTOM_TYPE:
    def __init__(self, data):
        self.data = data

# Use in node:
RETURN_TYPES = ("MY_CUSTOM_TYPE",)

Complete Example

class AdvancedImageProcessor:
    """
    Processes images with multiple effects and options.
    Demonstrates most node interface features.
    """
    
    def __init__(self):
        self.type = "AdvancedImageProcessor"
    
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "image": ("IMAGE", {
                    "tooltip": "Input image to process"
                }),
                "mode": (["blur", "sharpen", "edge_detect"], {
                    "default": "blur"
                }),
                "strength": ("FLOAT", {
                    "default": 1.0,
                    "min": 0.0,
                    "max": 10.0,
                    "step": 0.1,
                    "display": "slider"
                }),
            },
            "optional": {
                "mask": ("MASK", {
                    "tooltip": "Optional mask for selective processing"
                }),
                "seed": ("INT", {
                    "default": 0,
                    "min": 0,
                    "max": 0xffffffffffffffff
                }),
            },
            "hidden": {
                "unique_id": "UNIQUE_ID"
            }
        }
    
    RETURN_TYPES = ("IMAGE", "MASK")
    RETURN_NAMES = ("processed", "effect_mask")
    OUTPUT_TOOLTIPS = ("Processed image", "Mask showing affected areas")
    
    FUNCTION = "process_image"
    CATEGORY = "image/processing"
    DESCRIPTION = "Advanced image processing with multiple effects"
    
    def process_image(self, image, mode, strength, mask=None, seed=0, unique_id=None):
        # Processing implementation
        processed = apply_effect(image, mode, strength)
        effect_mask = generate_effect_mask(image, processed)
        
        return (processed, effect_mask)
    
    @classmethod
    def IS_CHANGED(cls, **kwargs):
        # Re-execute if seed changes
        return kwargs.get("seed", 0)
    
    @classmethod
    def VALIDATE_INPUTS(cls, **kwargs):
        if kwargs.get("strength", 0) < 0:
            return "Strength must be non-negative"
        return True

# Register the node
NODE_CLASS_MAPPINGS = {
    "AdvancedImageProcessor": AdvancedImageProcessor
}

NODE_DISPLAY_NAME_MAPPINGS = {
    "AdvancedImageProcessor": "Advanced Image Processor"
}

Server Communication

For advanced use cases, communicate with the frontend:

from server import PromptServer

def process_image(self, image):
    # Send message to frontend
    PromptServer.instance.send_sync(
        "my_node.message",
        {"text": "Processing complete", "node_id": unique_id}
    )
    return (image,)

Next Steps