ComfyUI Custom Node Development
This skill helps you create custom ComfyUI nodes from Python code.
Quick Template
class MyNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"value": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0}),
},
"optional": {
"mask": ("MASK",),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("output",)
FUNCTION = "execute"
CATEGORY = "Custom/MyNodes"
def execute(self, image, value, mask=None):
result = image * value
return (result,)
NODE_CLASS_MAPPINGS = {"MyNode": MyNode}
NODE_DISPLAY_NAME_MAPPINGS = {"MyNode": "My Node"}
Converting Python to Node
When you have Python code to wrap:
Step 1: Identify inputs and outputs
# Original function
def apply_blur(image, radius=5):
from PIL import ImageFilter
return image.filter(ImageFilter.GaussianBlur(radius))
Step 2: Map types
| Python Type |
ComfyUI Type |
Conversion |
| PIL Image |
IMAGE |
torch.from_numpy(np.array(pil) / 255.0) |
| numpy array |
IMAGE |
torch.from_numpy(arr.astype(np.float32)) |
| cv2 BGR |
IMAGE |
torch.from_numpy(cv2.cvtColor(img, cv2.COLOR_BGR2RGB) / 255.0) |
| float 0-255 |
IMAGE |
Divide by 255.0 |
| Single image |
Batch |
tensor.unsqueeze(0) |
Step 3: Handle batch dimension
ComfyUI images are [B,H,W,C] - always process all batch items:
def execute(self, image, radius):
batch_results = []
for i in range(image.shape[0]):
# Convert to PIL
img_np = (image[i].cpu().numpy() * 255).astype(np.uint8)
pil_img = Image.fromarray(img_np)
# Your processing
result = pil_img.filter(ImageFilter.GaussianBlur(radius))
# Convert back
result_np = np.array(result).astype(np.float32) / 255.0
batch_results.append(torch.from_numpy(result_np))
return (torch.stack(batch_results),)
Common Input Types
| Type |
Shape/Format |
Widget Options |
| IMAGE |
[B,H,W,C] float 0-1 |
- |
| MASK |
[H,W] or [B,H,W] float 0-1 |
- |
| LATENT |
{"samples": [B,C,H,W]} |
- |
| MODEL |
ModelPatcher |
- |
| CLIP |
CLIP encoder |
- |
| VAE |
VAE model |
- |
| CONDITIONING |
[(cond, pooled), ...] |
- |
| INT |
integer |
default, min, max, step |
| FLOAT |
float |
default, min, max, step, display |
| STRING |
str |
default, multiline |
| BOOLEAN |
bool |
default |
| COMBO |
str |
List of options as type |
Checklist
References
- NODE_TEMPLATE.md - Full template with V3 schema
- OFFICIAL_DOCS.md - Official ComfyUI documentation
- PURZ_EXAMPLES.md - Example nodes and workflows
Finding Similar Nodes
Use the MCP tools to find existing nodes for reference:
comfy_search("blur") → Find blur implementations
comfy_spec("GaussianBlur") → See how inputs are defined
1---2name: comfy-nodes3description: Use when the user wants to create a ComfyUI custom node, convert Python code to a node, make a node from a script, or needs help with ComfyUI node development, INPUT_TYPES, RETURN_TYPES, or node class structure.4---56# ComfyUI Custom Node Development78This skill helps you create custom ComfyUI nodes from Python code.910## Quick Template1112```python13class MyNode:14 @classmethod15 def INPUT_TYPES(cls):16 return {17 "required": {18 "image": ("IMAGE",),19 "value": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0}),20 },21 "optional": {22 "mask": ("MASK",),23 }24 }2526 RETURN_TYPES = ("IMAGE",)27 RETURN_NAMES = ("output",)28 FUNCTION = "execute"29 CATEGORY = "Custom/MyNodes"3031 def execute(self, image, value, mask=None):32 result = image * value33 return (result,)3435NODE_CLASS_MAPPINGS = {"MyNode": MyNode}36NODE_DISPLAY_NAME_MAPPINGS = {"MyNode": "My Node"}37```3839## Converting Python to Node4041When you have Python code to wrap:4243### Step 1: Identify inputs and outputs44```python45# Original function46def apply_blur(image, radius=5):47 from PIL import ImageFilter48 return image.filter(ImageFilter.GaussianBlur(radius))49```5051### Step 2: Map types5253| Python Type | ComfyUI Type | Conversion |54|-------------|--------------|------------|55| PIL Image | IMAGE | `torch.from_numpy(np.array(pil) / 255.0)` |56| numpy array | IMAGE | `torch.from_numpy(arr.astype(np.float32))` |57| cv2 BGR | IMAGE | `torch.from_numpy(cv2.cvtColor(img, cv2.COLOR_BGR2RGB) / 255.0)` |58| float 0-255 | IMAGE | Divide by 255.0 |59| Single image | Batch | `tensor.unsqueeze(0)` |6061### Step 3: Handle batch dimension6263ComfyUI images are `[B,H,W,C]` - always process all batch items:6465```python66def execute(self, image, radius):67 batch_results = []68 for i in range(image.shape[0]):69 # Convert to PIL70 img_np = (image[i].cpu().numpy() * 255).astype(np.uint8)71 pil_img = Image.fromarray(img_np)7273 # Your processing74 result = pil_img.filter(ImageFilter.GaussianBlur(radius))7576 # Convert back77 result_np = np.array(result).astype(np.float32) / 255.078 batch_results.append(torch.from_numpy(result_np))7980 return (torch.stack(batch_results),)81```8283## Common Input Types8485| Type | Shape/Format | Widget Options |86|------|--------------|----------------|87| IMAGE | [B,H,W,C] float 0-1 | - |88| MASK | [H,W] or [B,H,W] float 0-1 | - |89| LATENT | {"samples": [B,C,H,W]} | - |90| MODEL | ModelPatcher | - |91| CLIP | CLIP encoder | - |92| VAE | VAE model | - |93| CONDITIONING | [(cond, pooled), ...] | - |94| INT | integer | default, min, max, step |95| FLOAT | float | default, min, max, step, display |96| STRING | str | default, multiline |97| BOOLEAN | bool | default |98| COMBO | str | List of options as type |99100## Checklist101102- [ ] `INPUT_TYPES` is a `@classmethod`103- [ ] Return value is a tuple: `return (result,)`104- [ ] Handle batch dimension `[B,H,W,C]`105- [ ] Add to `NODE_CLASS_MAPPINGS`106- [ ] Category uses `/` for submenus107108## References109110- [NODE_TEMPLATE.md](references/NODE_TEMPLATE.md) - Full template with V3 schema111- [OFFICIAL_DOCS.md](references/OFFICIAL_DOCS.md) - Official ComfyUI documentation112- [PURZ_EXAMPLES.md](references/PURZ_EXAMPLES.md) - Example nodes and workflows113114## Finding Similar Nodes115116Use the MCP tools to find existing nodes for reference:117118```119comfy_search("blur") → Find blur implementations120comfy_spec("GaussianBlur") → See how inputs are defined121```