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
- Learn about Data Types and Type Conversion
- Explore Advanced Features
- See Example Implementations