2025-03-05 15:35:26 -05:00
from __future__ import annotations
2023-11-18 04:44:17 -05:00
import nodes
2023-11-23 13:55:29 -05:00
import folder_paths
import json
import os
2025-05-09 10:46:34 -07:00
import re
2026-02-23 18:01:17 -08:00
import math
2025-05-18 01:09:56 -07:00
import torch
2025-06-01 01:28:52 -07:00
import comfy . utils
2023-11-23 13:55:29 -05:00
2025-06-02 18:57:50 -07:00
from server import PromptServer
2025-12-27 05:39:02 +02:00
from comfy_api . latest import ComfyExtension , IO , UI
from typing_extensions import override
SVG = IO . SVG . Type # TODO: temporary solution for backward compatibility, will be removed later.
2025-03-05 15:35:26 -05:00
2023-11-18 04:44:17 -05:00
MAX_RESOLUTION = nodes . MAX_RESOLUTION
2025-12-27 05:39:02 +02:00
class ImageCrop ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageCrop " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " trim " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Crop Image (DEPRECATED) " ,
2025-12-27 05:39:02 +02:00
category = " image/transform " ,
2026-02-17 20:13:39 -05:00
is_deprecated = True ,
feat: add essentials_category (#12357)
* feat: add essentials_category field to node schema
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* feat: add ESSENTIALS_CATEGORY to core nodes
Marked nodes:
- Basic: LoadImage, SaveImage, LoadVideo, SaveVideo, Load3D, CLIPTextEncode
- Image Tools: ImageScale, ImageInvert, ImageBatch, ImageCrop, ImageRotate, ImageBlur
- Image Tools/Preprocessing: Canny
- Image Generation: LoraLoader
- Audio: LoadAudio, SaveAudio
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* Add ESSENTIALS_CATEGORY to more nodes
- SaveGLB (Basic)
- GetVideoComponents (Video Tools)
- TencentTextToModelNode, TencentImageToModelNode (3D)
- RecraftRemoveBackgroundNode (Image Tools)
- KlingLipSyncAudioToVideoNode (Video Generation)
- OpenAIChatNode (Text Generation)
- StabilityTextToAudio (Audio)
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b69-81c1-71c3-8096-450a39e20910
* fix: correct essentials category for Canny node
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: replace essentials_category string literals with constants
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: revert constants, use string literals for essentials_category
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* fix: update basics
---------
Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-20 11:00:26 +08:00
essentials_category = " Image Tools " ,
2025-12-27 05:39:02 +02:00
inputs = [
IO . Image . Input ( " image " ) ,
IO . Int . Input ( " width " , default = 512 , min = 1 , max = nodes . MAX_RESOLUTION , step = 1 ) ,
IO . Int . Input ( " height " , default = 512 , min = 1 , max = nodes . MAX_RESOLUTION , step = 1 ) ,
IO . Int . Input ( " x " , default = 0 , min = 0 , max = nodes . MAX_RESOLUTION , step = 1 ) ,
IO . Int . Input ( " y " , default = 0 , min = 0 , max = nodes . MAX_RESOLUTION , step = 1 ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
2023-11-18 04:44:17 -05:00
@classmethod
2025-12-27 05:39:02 +02:00
def execute ( cls , image , width , height , x , y ) - > IO . NodeOutput :
2023-11-18 04:44:17 -05:00
x = min ( x , image . shape [ 2 ] - 1 )
y = min ( y , image . shape [ 1 ] - 1 )
to_x = width + x
to_y = height + y
img = image [ : , y : to_y , x : to_x , : ]
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( img )
2023-11-18 04:44:17 -05:00
2025-12-27 05:39:02 +02:00
crop = execute # TODO: remove
2023-11-19 06:09:01 -05:00
2026-02-17 20:13:39 -05:00
class ImageCropV2 ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageCropV2 " ,
2026-05-21 15:14:16 +08:00
search_aliases = [ " crop " , " cut " , " trim " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Crop Image " ,
2026-02-17 20:13:39 -05:00
category = " image/transform " ,
2026-05-21 15:14:16 +08:00
description = " Crop an image to the specified dimensions. " ,
2026-03-15 16:18:04 -07:00
essentials_category = " Image Tools " ,
2026-03-27 21:06:38 -04:00
has_intermediate_output = True ,
2026-02-17 20:13:39 -05:00
inputs = [
IO . Image . Input ( " image " ) ,
IO . BoundingBox . Input ( " crop_region " , component = " ImageCrop " ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
@classmethod
def execute ( cls , image , crop_region ) - > IO . NodeOutput :
x = crop_region . get ( " x " , 0 )
y = crop_region . get ( " y " , 0 )
width = crop_region . get ( " width " , 512 )
height = crop_region . get ( " height " , 512 )
x = min ( x , image . shape [ 2 ] - 1 )
y = min ( y , image . shape [ 1 ] - 1 )
to_x = width + x
to_y = height + y
img = image [ : , y : to_y , x : to_x , : ]
return IO . NodeOutput ( img , ui = UI . PreviewImage ( img ) )
class BoundingBox ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " PrimitiveBoundingBox " ,
display_name = " Bounding Box " ,
category = " utils/primitive " ,
inputs = [
IO . Int . Input ( " x " , default = 0 , min = 0 , max = MAX_RESOLUTION ) ,
IO . Int . Input ( " y " , default = 0 , min = 0 , max = MAX_RESOLUTION ) ,
IO . Int . Input ( " width " , default = 512 , min = 1 , max = MAX_RESOLUTION ) ,
IO . Int . Input ( " height " , default = 512 , min = 1 , max = MAX_RESOLUTION ) ,
] ,
outputs = [ IO . BoundingBox . Output ( ) ] ,
)
@classmethod
def execute ( cls , x , y , width , height ) - > IO . NodeOutput :
return IO . NodeOutput ( { " x " : x , " y " : y , " width " : width , " height " : height } )
2025-12-27 05:39:02 +02:00
class RepeatImageBatch ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " RepeatImageBatch " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " duplicate image " , " clone image " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Repeat Image Batch " ,
2025-12-27 05:39:02 +02:00
category = " image/batch " ,
inputs = [
IO . Image . Input ( " image " ) ,
IO . Int . Input ( " amount " , default = 1 , min = 1 , max = 4096 ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
2023-11-18 04:44:17 -05:00
2024-02-12 12:46:15 -05:00
@classmethod
2025-12-27 05:39:02 +02:00
def execute ( cls , image , amount ) - > IO . NodeOutput :
s = image . repeat ( ( amount , 1 , 1 , 1 ) )
return IO . NodeOutput ( s )
2024-02-12 12:46:15 -05:00
2025-12-27 05:39:02 +02:00
repeat = execute # TODO: remove
2024-02-12 12:46:15 -05:00
2025-12-27 05:39:02 +02:00
class ImageFromBatch ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageFromBatch " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " select image " , " pick from batch " , " extract image " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Get Image from Batch " ,
2025-12-27 05:39:02 +02:00
category = " image/batch " ,
inputs = [
IO . Image . Input ( " image " ) ,
2026-05-15 08:30:02 -06:00
IO . Int . Input ( " batch_index " , default = 0 , min = - MAX_RESOLUTION , max = MAX_RESOLUTION ) ,
2025-12-27 05:39:02 +02:00
IO . Int . Input ( " length " , default = 1 , min = 1 , max = 4096 ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
@classmethod
def execute ( cls , image , batch_index , length ) - > IO . NodeOutput :
2024-02-12 12:46:15 -05:00
s_in = image
2026-05-15 08:30:02 -06:00
if batch_index < 0 :
batch_index + = s_in . shape [ 0 ]
batch_index = max ( 0 , min ( s_in . shape [ 0 ] - 1 , batch_index ) )
2024-02-12 12:46:15 -05:00
length = min ( s_in . shape [ 0 ] - batch_index , length )
s = s_in [ batch_index : batch_index + length ] . clone ( )
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( s )
2024-02-12 12:46:15 -05:00
2025-12-27 05:39:02 +02:00
frombatch = execute # TODO: remove
2025-05-18 01:09:56 -07:00
2025-12-27 05:39:02 +02:00
class ImageAddNoise ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageAddNoise " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " film grain " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Add Noise to Image " ,
2026-05-19 12:13:48 +08:00
category = " image/filters " ,
2025-12-27 05:39:02 +02:00
inputs = [
IO . Image . Input ( " image " ) ,
IO . Int . Input (
" seed " ,
default = 0 ,
min = 0 ,
max = 0xFFFFFFFFFFFFFFFF ,
control_after_generate = True ,
tooltip = " The random seed used for creating the noise. " ,
) ,
IO . Float . Input ( " strength " , default = 0.5 , min = 0.0 , max = 1.0 , step = 0.01 ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
2025-05-18 01:09:56 -07:00
2025-12-27 05:39:02 +02:00
@classmethod
def execute ( cls , image , seed , strength ) - > IO . NodeOutput :
2025-05-18 01:09:56 -07:00
generator = torch . manual_seed ( seed )
s = torch . clip ( ( image + strength * torch . randn ( image . size ( ) , generator = generator , device = " cpu " ) . to ( image ) ) , min = 0.0 , max = 1.0 )
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( s )
repeat = execute # TODO: remove
2025-05-18 01:09:56 -07:00
2025-12-27 05:39:02 +02:00
class SaveAnimatedWEBP ( IO . ComfyNode ) :
COMPRESS_METHODS = { " default " : 4 , " fastest " : 0 , " slowest " : 6 }
2023-11-23 13:55:29 -05:00
@classmethod
2025-12-27 05:39:02 +02:00
def define_schema ( cls ) :
return IO . Schema (
node_id = " SaveAnimatedWEBP " ,
2026-05-19 12:13:48 +08:00
display_name = " Save Animated WEBP " ,
category = " image " ,
2025-12-27 05:39:02 +02:00
inputs = [
IO . Image . Input ( " images " ) ,
IO . String . Input ( " filename_prefix " , default = " ComfyUI " ) ,
IO . Float . Input ( " fps " , default = 6.0 , min = 0.01 , max = 1000.0 , step = 0.01 ) ,
IO . Boolean . Input ( " lossless " , default = True ) ,
IO . Int . Input ( " quality " , default = 80 , min = 0 , max = 100 ) ,
IO . Combo . Input ( " method " , options = list ( cls . COMPRESS_METHODS . keys ( ) ) ) ,
# "num_frames": ("INT", {"default": 0, "min": 0, "max": 8192}),
] ,
hidden = [ IO . Hidden . prompt , IO . Hidden . extra_pnginfo ] ,
is_output_node = True ,
)
2023-11-23 13:55:29 -05:00
2025-12-27 05:39:02 +02:00
@classmethod
def execute ( cls , images , fps , filename_prefix , lossless , quality , method , num_frames = 0 ) - > IO . NodeOutput :
return IO . NodeOutput (
ui = UI . ImageSaveHelper . get_save_animated_webp_ui (
images = images ,
filename_prefix = filename_prefix ,
cls = cls ,
fps = fps ,
lossless = lossless ,
quality = quality ,
method = cls . COMPRESS_METHODS . get ( method )
)
)
save_images = execute # TODO: remove
2023-11-23 13:55:29 -05:00
2025-12-27 05:39:02 +02:00
class SaveAnimatedPNG ( IO . ComfyNode ) :
2023-11-24 11:12:10 -05:00
@classmethod
2025-12-27 05:39:02 +02:00
def define_schema ( cls ) :
return IO . Schema (
node_id = " SaveAnimatedPNG " ,
2026-05-19 12:13:48 +08:00
display_name = " Save Animated PNG " ,
category = " image " ,
2025-12-27 05:39:02 +02:00
inputs = [
IO . Image . Input ( " images " ) ,
IO . String . Input ( " filename_prefix " , default = " ComfyUI " ) ,
IO . Float . Input ( " fps " , default = 6.0 , min = 0.01 , max = 1000.0 , step = 0.01 ) ,
feat: mark 429 widgets as advanced for collapsible UI (#12197)
* feat: mark 429 widgets as advanced for collapsible UI
Mark widgets as advanced across core, comfy_extras, and comfy_api_nodes
to support the new collapsible advanced inputs section in the frontend.
Changes:
- 267 advanced markers in comfy_extras/
- 162 advanced markers in comfy_api_nodes/
- All files pass python3 -m py_compile verification
Widgets marked advanced (hidden by default):
- Scheduler internals: sigma_max, sigma_min, rho, mu, beta, alpha
- Sampler internals: eta, s_noise, order, rtol, atol, h_init, pcoeff, etc.
- Memory optimization: tile_size, overlap, temporal_size, temporal_overlap
- Pipeline controls: add_noise, start_at_step, end_at_step
- Timing controls: start_percent, end_percent
- Layer selection: stop_at_clip_layer, layers, block_number
- Video encoding: codec, crf, format
- Device/dtype: device, noise_device, dtype, weight_dtype
Widgets kept basic (always visible):
- Core params: strength, steps, cfg, denoise, seed, width, height
- Model selectors: ckpt_name, lora_name, vae_name, sampler_name
- Common controls: upscale_method, crop, batch_size, fps, opacity
Related: frontend PR #11939
Amp-Thread-ID: https://ampcode.com/threads/T-019c1734-6b61-702e-b333-f02c399963fc
* fix: remove advanced=True from DynamicCombo.Input (unsupported)
Amp-Thread-ID: https://ampcode.com/threads/T-019c1734-6b61-702e-b333-f02c399963fc
* fix: address review - un-mark model merge, video, image, and training node widgets as advanced
Per comfyanonymous review:
- Model merge arguments should not be advanced (all 14 model-specific merge classes)
- SaveAnimatedWEBP lossless/quality/method should not be advanced
- SaveWEBM/SaveVideo codec/crf/format should not be advanced
- TrainLoraNode options should not be advanced (7 inputs)
Amp-Thread-ID: https://ampcode.com/threads/T-019c322b-a3a8-71b7-9962-d44573ca6352
* fix: un-mark batch_size and webcam width/height as advanced (should stay basic)
Amp-Thread-ID: https://ampcode.com/threads/T-019c3236-1417-74aa-82a3-bcb365fbe9d1
---------
Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-19 19:20:02 -08:00
IO . Int . Input ( " compress_level " , default = 4 , min = 0 , max = 9 , advanced = True ) ,
2025-12-27 05:39:02 +02:00
] ,
hidden = [ IO . Hidden . prompt , IO . Hidden . extra_pnginfo ] ,
is_output_node = True ,
)
@classmethod
def execute ( cls , images , fps , compress_level , filename_prefix = " ComfyUI " ) - > IO . NodeOutput :
return IO . NodeOutput (
ui = UI . ImageSaveHelper . get_save_animated_png_ui (
images = images ,
filename_prefix = filename_prefix ,
cls = cls ,
fps = fps ,
compress_level = compress_level ,
)
)
save_images = execute # TODO: remove
class ImageStitch ( IO . ComfyNode ) :
2025-06-01 01:28:52 -07:00
""" Upstreamed from https://github.com/kijai/ComfyUI-KJNodes """
@classmethod
2025-12-27 05:39:02 +02:00
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageStitch " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " combine images " , " join images " , " concatenate images " , " side by side " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Stitch Images " ,
2025-12-27 05:39:02 +02:00
description = " Stitches image2 to image1 in the specified direction. \n "
" If image2 is not provided, returns image1 unchanged. \n "
" Optional spacing can be added between images. " ,
category = " image/transform " ,
inputs = [
IO . Image . Input ( " image1 " ) ,
IO . Combo . Input ( " direction " , options = [ " right " , " down " , " left " , " up " ] , default = " right " ) ,
IO . Boolean . Input ( " match_image_size " , default = True ) ,
feat: mark 429 widgets as advanced for collapsible UI (#12197)
* feat: mark 429 widgets as advanced for collapsible UI
Mark widgets as advanced across core, comfy_extras, and comfy_api_nodes
to support the new collapsible advanced inputs section in the frontend.
Changes:
- 267 advanced markers in comfy_extras/
- 162 advanced markers in comfy_api_nodes/
- All files pass python3 -m py_compile verification
Widgets marked advanced (hidden by default):
- Scheduler internals: sigma_max, sigma_min, rho, mu, beta, alpha
- Sampler internals: eta, s_noise, order, rtol, atol, h_init, pcoeff, etc.
- Memory optimization: tile_size, overlap, temporal_size, temporal_overlap
- Pipeline controls: add_noise, start_at_step, end_at_step
- Timing controls: start_percent, end_percent
- Layer selection: stop_at_clip_layer, layers, block_number
- Video encoding: codec, crf, format
- Device/dtype: device, noise_device, dtype, weight_dtype
Widgets kept basic (always visible):
- Core params: strength, steps, cfg, denoise, seed, width, height
- Model selectors: ckpt_name, lora_name, vae_name, sampler_name
- Common controls: upscale_method, crop, batch_size, fps, opacity
Related: frontend PR #11939
Amp-Thread-ID: https://ampcode.com/threads/T-019c1734-6b61-702e-b333-f02c399963fc
* fix: remove advanced=True from DynamicCombo.Input (unsupported)
Amp-Thread-ID: https://ampcode.com/threads/T-019c1734-6b61-702e-b333-f02c399963fc
* fix: address review - un-mark model merge, video, image, and training node widgets as advanced
Per comfyanonymous review:
- Model merge arguments should not be advanced (all 14 model-specific merge classes)
- SaveAnimatedWEBP lossless/quality/method should not be advanced
- SaveWEBM/SaveVideo codec/crf/format should not be advanced
- TrainLoraNode options should not be advanced (7 inputs)
Amp-Thread-ID: https://ampcode.com/threads/T-019c322b-a3a8-71b7-9962-d44573ca6352
* fix: un-mark batch_size and webcam width/height as advanced (should stay basic)
Amp-Thread-ID: https://ampcode.com/threads/T-019c3236-1417-74aa-82a3-bcb365fbe9d1
---------
Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-19 19:20:02 -08:00
IO . Int . Input ( " spacing_width " , default = 0 , min = 0 , max = 1024 , step = 2 , advanced = True ) ,
IO . Combo . Input ( " spacing_color " , options = [ " white " , " black " , " red " , " green " , " blue " ] , default = " white " , advanced = True ) ,
2025-12-27 05:39:02 +02:00
IO . Image . Input ( " image2 " , optional = True ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
2025-06-01 01:28:52 -07:00
2025-12-27 05:39:02 +02:00
@classmethod
def execute (
cls ,
2025-06-01 01:28:52 -07:00
image1 ,
direction ,
match_image_size ,
spacing_width ,
spacing_color ,
image2 = None ,
2025-12-27 05:39:02 +02:00
) - > IO . NodeOutput :
2025-06-01 01:28:52 -07:00
if image2 is None :
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( image1 )
2025-06-01 01:28:52 -07:00
# Handle batch size differences
if image1 . shape [ 0 ] != image2 . shape [ 0 ] :
max_batch = max ( image1 . shape [ 0 ] , image2 . shape [ 0 ] )
if image1 . shape [ 0 ] < max_batch :
image1 = torch . cat (
[ image1 , image1 [ - 1 : ] . repeat ( max_batch - image1 . shape [ 0 ] , 1 , 1 , 1 ) ]
)
if image2 . shape [ 0 ] < max_batch :
image2 = torch . cat (
[ image2 , image2 [ - 1 : ] . repeat ( max_batch - image2 . shape [ 0 ] , 1 , 1 , 1 ) ]
)
# Match image sizes if requested
if match_image_size :
h1 , w1 = image1 . shape [ 1 : 3 ]
h2 , w2 = image2 . shape [ 1 : 3 ]
aspect_ratio = w2 / h2
if direction in [ " left " , " right " ] :
target_h , target_w = h1 , int ( h1 * aspect_ratio )
else : # up, down
target_w , target_h = w1 , int ( w1 / aspect_ratio )
image2 = comfy . utils . common_upscale (
image2 . movedim ( - 1 , 1 ) , target_w , target_h , " lanczos " , " disabled "
) . movedim ( 1 , - 1 )
2025-06-21 21:19:41 -07:00
color_map = {
" white " : 1.0 ,
" black " : 0.0 ,
" red " : ( 1.0 , 0.0 , 0.0 ) ,
" green " : ( 0.0 , 1.0 , 0.0 ) ,
" blue " : ( 0.0 , 0.0 , 1.0 ) ,
}
color_val = color_map [ spacing_color ]
2025-06-01 01:28:52 -07:00
# When not matching sizes, pad to align non-concat dimensions
if not match_image_size :
h1 , w1 = image1 . shape [ 1 : 3 ]
h2 , w2 = image2 . shape [ 1 : 3 ]
2025-06-21 21:19:41 -07:00
pad_value = 0.0
if not isinstance ( color_val , tuple ) :
pad_value = color_val
2025-06-01 01:28:52 -07:00
if direction in [ " left " , " right " ] :
# For horizontal concat, pad heights to match
if h1 != h2 :
target_h = max ( h1 , h2 )
if h1 < target_h :
pad_h = target_h - h1
pad_top , pad_bottom = pad_h / / 2 , pad_h - pad_h / / 2
2025-06-21 21:19:41 -07:00
image1 = torch . nn . functional . pad ( image1 , ( 0 , 0 , 0 , 0 , pad_top , pad_bottom ) , mode = ' constant ' , value = pad_value )
2025-06-01 01:28:52 -07:00
if h2 < target_h :
pad_h = target_h - h2
pad_top , pad_bottom = pad_h / / 2 , pad_h - pad_h / / 2
2025-06-21 21:19:41 -07:00
image2 = torch . nn . functional . pad ( image2 , ( 0 , 0 , 0 , 0 , pad_top , pad_bottom ) , mode = ' constant ' , value = pad_value )
2025-06-01 01:28:52 -07:00
else : # up, down
# For vertical concat, pad widths to match
if w1 != w2 :
target_w = max ( w1 , w2 )
if w1 < target_w :
pad_w = target_w - w1
pad_left , pad_right = pad_w / / 2 , pad_w - pad_w / / 2
2025-06-21 21:19:41 -07:00
image1 = torch . nn . functional . pad ( image1 , ( 0 , 0 , pad_left , pad_right ) , mode = ' constant ' , value = pad_value )
2025-06-01 01:28:52 -07:00
if w2 < target_w :
pad_w = target_w - w2
pad_left , pad_right = pad_w / / 2 , pad_w - pad_w / / 2
2025-06-21 21:19:41 -07:00
image2 = torch . nn . functional . pad ( image2 , ( 0 , 0 , pad_left , pad_right ) , mode = ' constant ' , value = pad_value )
2025-06-01 01:28:52 -07:00
# Ensure same number of channels
if image1 . shape [ - 1 ] != image2 . shape [ - 1 ] :
max_channels = max ( image1 . shape [ - 1 ] , image2 . shape [ - 1 ] )
if image1 . shape [ - 1 ] < max_channels :
image1 = torch . cat (
[
image1 ,
torch . ones (
* image1 . shape [ : - 1 ] ,
max_channels - image1 . shape [ - 1 ] ,
device = image1 . device ,
) ,
] ,
dim = - 1 ,
)
if image2 . shape [ - 1 ] < max_channels :
image2 = torch . cat (
[
image2 ,
torch . ones (
* image2 . shape [ : - 1 ] ,
max_channels - image2 . shape [ - 1 ] ,
device = image2 . device ,
) ,
] ,
dim = - 1 ,
)
# Add spacing if specified
if spacing_width > 0 :
spacing_width = spacing_width + ( spacing_width % 2 ) # Ensure even
if direction in [ " left " , " right " ] :
spacing_shape = (
image1 . shape [ 0 ] ,
max ( image1 . shape [ 1 ] , image2 . shape [ 1 ] ) ,
spacing_width ,
image1 . shape [ - 1 ] ,
)
else :
spacing_shape = (
image1 . shape [ 0 ] ,
spacing_width ,
max ( image1 . shape [ 2 ] , image2 . shape [ 2 ] ) ,
image1 . shape [ - 1 ] ,
)
spacing = torch . full ( spacing_shape , 0.0 , device = image1 . device )
if isinstance ( color_val , tuple ) :
for i , c in enumerate ( color_val ) :
if i < spacing . shape [ - 1 ] :
spacing [ . . . , i ] = c
if spacing . shape [ - 1 ] == 4 : # Add alpha
spacing [ . . . , 3 ] = 1.0
else :
spacing [ . . . , : min ( 3 , spacing . shape [ - 1 ] ) ] = color_val
if spacing . shape [ - 1 ] == 4 :
spacing [ . . . , 3 ] = 1.0
# Concatenate images
images = [ image2 , image1 ] if direction in [ " left " , " up " ] else [ image1 , image2 ]
if spacing_width > 0 :
images . insert ( 1 , spacing )
concat_dim = 2 if direction in [ " left " , " right " ] else 1
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( torch . cat ( images , dim = concat_dim ) )
stitch = execute # TODO: remove
2025-06-01 01:28:52 -07:00
2025-06-22 14:59:31 -07:00
2025-12-27 05:39:02 +02:00
class ResizeAndPadImage ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ResizeAndPadImage " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " fit to size " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Resize And Pad Image " ,
2025-12-27 05:39:02 +02:00
category = " image/transform " ,
inputs = [
IO . Image . Input ( " image " ) ,
IO . Int . Input ( " target_width " , default = 512 , min = 1 , max = nodes . MAX_RESOLUTION , step = 1 ) ,
IO . Int . Input ( " target_height " , default = 512 , min = 1 , max = nodes . MAX_RESOLUTION , step = 1 ) ,
feat: mark 429 widgets as advanced for collapsible UI (#12197)
* feat: mark 429 widgets as advanced for collapsible UI
Mark widgets as advanced across core, comfy_extras, and comfy_api_nodes
to support the new collapsible advanced inputs section in the frontend.
Changes:
- 267 advanced markers in comfy_extras/
- 162 advanced markers in comfy_api_nodes/
- All files pass python3 -m py_compile verification
Widgets marked advanced (hidden by default):
- Scheduler internals: sigma_max, sigma_min, rho, mu, beta, alpha
- Sampler internals: eta, s_noise, order, rtol, atol, h_init, pcoeff, etc.
- Memory optimization: tile_size, overlap, temporal_size, temporal_overlap
- Pipeline controls: add_noise, start_at_step, end_at_step
- Timing controls: start_percent, end_percent
- Layer selection: stop_at_clip_layer, layers, block_number
- Video encoding: codec, crf, format
- Device/dtype: device, noise_device, dtype, weight_dtype
Widgets kept basic (always visible):
- Core params: strength, steps, cfg, denoise, seed, width, height
- Model selectors: ckpt_name, lora_name, vae_name, sampler_name
- Common controls: upscale_method, crop, batch_size, fps, opacity
Related: frontend PR #11939
Amp-Thread-ID: https://ampcode.com/threads/T-019c1734-6b61-702e-b333-f02c399963fc
* fix: remove advanced=True from DynamicCombo.Input (unsupported)
Amp-Thread-ID: https://ampcode.com/threads/T-019c1734-6b61-702e-b333-f02c399963fc
* fix: address review - un-mark model merge, video, image, and training node widgets as advanced
Per comfyanonymous review:
- Model merge arguments should not be advanced (all 14 model-specific merge classes)
- SaveAnimatedWEBP lossless/quality/method should not be advanced
- SaveWEBM/SaveVideo codec/crf/format should not be advanced
- TrainLoraNode options should not be advanced (7 inputs)
Amp-Thread-ID: https://ampcode.com/threads/T-019c322b-a3a8-71b7-9962-d44573ca6352
* fix: un-mark batch_size and webcam width/height as advanced (should stay basic)
Amp-Thread-ID: https://ampcode.com/threads/T-019c3236-1417-74aa-82a3-bcb365fbe9d1
---------
Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-19 19:20:02 -08:00
IO . Combo . Input ( " padding_color " , options = [ " white " , " black " ] , advanced = True ) ,
IO . Combo . Input ( " interpolation " , options = [ " area " , " bicubic " , " nearest-exact " , " bilinear " , " lanczos " ] , advanced = True ) ,
2025-12-27 05:39:02 +02:00
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
@classmethod
def execute ( cls , image , target_width , target_height , padding_color , interpolation ) - > IO . NodeOutput :
2025-06-22 14:59:31 -07:00
batch_size , orig_height , orig_width , channels = image . shape
scale_w = target_width / orig_width
scale_h = target_height / orig_height
scale = min ( scale_w , scale_h )
new_width = int ( orig_width * scale )
new_height = int ( orig_height * scale )
image_permuted = image . permute ( 0 , 3 , 1 , 2 )
resized = comfy . utils . common_upscale ( image_permuted , new_width , new_height , interpolation , " disabled " )
pad_value = 0.0 if padding_color == " black " else 1.0
padded = torch . full (
( batch_size , channels , target_height , target_width ) ,
pad_value ,
dtype = image . dtype ,
device = image . device
)
y_offset = ( target_height - new_height ) / / 2
x_offset = ( target_width - new_width ) / / 2
padded [ : , : , y_offset : y_offset + new_height , x_offset : x_offset + new_width ] = resized
output = padded . permute ( 0 , 2 , 3 , 1 )
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( output )
2025-06-01 01:28:52 -07:00
2025-12-27 05:39:02 +02:00
resize_and_pad = execute # TODO: remove
2025-05-09 10:46:34 -07:00
2025-12-27 05:39:02 +02:00
class SaveSVGNode ( IO . ComfyNode ) :
2025-05-09 10:46:34 -07:00
@classmethod
2025-12-27 05:39:02 +02:00
def define_schema ( cls ) :
return IO . Schema (
node_id = " SaveSVGNode " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " export vector " , " save vector graphics " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Save SVG " ,
2025-12-27 05:39:02 +02:00
description = " Save SVG files on disk. " ,
2026-05-19 12:13:48 +08:00
category = " image " ,
2025-12-27 05:39:02 +02:00
inputs = [
IO . SVG . Input ( " svg " ) ,
IO . String . Input (
" filename_prefix " ,
default = " svg/ComfyUI " ,
tooltip = " The prefix for the file to save. This may include formatting information such as %d ate:yyyy-MM-dd % o r %E mpty Latent Image.width % to include values from nodes. " ,
) ,
] ,
hidden = [ IO . Hidden . prompt , IO . Hidden . extra_pnginfo ] ,
is_output_node = True ,
)
2025-05-09 10:46:34 -07:00
2025-12-27 05:39:02 +02:00
@classmethod
def execute ( cls , svg : IO . SVG . Type , filename_prefix = " svg/ComfyUI " ) - > IO . NodeOutput :
full_output_folder , filename , counter , subfolder , filename_prefix = folder_paths . get_save_image_path ( filename_prefix , folder_paths . get_output_directory ( ) )
results : list [ UI . SavedResult ] = [ ]
2025-05-09 10:46:34 -07:00
# Prepare metadata JSON
metadata_dict = { }
2025-12-27 05:39:02 +02:00
if cls . hidden . prompt is not None :
metadata_dict [ " prompt " ] = cls . hidden . prompt
if cls . hidden . extra_pnginfo is not None :
metadata_dict . update ( cls . hidden . extra_pnginfo )
2025-05-09 10:46:34 -07:00
# Convert metadata to JSON string
metadata_json = json . dumps ( metadata_dict , indent = 2 ) if metadata_dict else None
2025-12-27 05:39:02 +02:00
2025-05-09 10:46:34 -07:00
for batch_number , svg_bytes in enumerate ( svg . data ) :
filename_with_batch_num = filename . replace ( " % batch_num % " , str ( batch_number ) )
file = f " { filename_with_batch_num } _ { counter : 05 } _.svg "
# Read SVG content
svg_bytes . seek ( 0 )
svg_content = svg_bytes . read ( ) . decode ( ' utf-8 ' )
# Inject metadata if available
if metadata_json :
# Create metadata element with CDATA section
metadata_element = f """ <metadata>
< ! [ CDATA [
{ metadata_json }
] ] >
< / metadata >
"""
# Insert metadata after opening svg tag using regex with a replacement function
def replacement ( match ) :
# match.group(1) contains the captured <svg> tag
return match . group ( 1 ) + ' \n ' + metadata_element
# Apply the substitution
svg_content = re . sub ( r ' (<svg[^>]*>) ' , replacement , svg_content , flags = re . UNICODE )
# Write the modified SVG to file
with open ( os . path . join ( full_output_folder , file ) , ' wb ' ) as svg_file :
svg_file . write ( svg_content . encode ( ' utf-8 ' ) )
2025-12-27 05:39:02 +02:00
results . append ( UI . SavedResult ( filename = file , subfolder = subfolder , type = IO . FolderType . output ) )
2025-05-09 10:46:34 -07:00
counter + = 1
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( ui = { " images " : results } )
2025-05-09 10:46:34 -07:00
2025-12-27 05:39:02 +02:00
save_svg = execute # TODO: remove
2025-06-02 18:57:50 -07:00
2025-12-27 05:39:02 +02:00
class GetImageSize ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " GetImageSize " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " dimensions " , " resolution " , " image info " ] ,
2025-12-27 05:39:02 +02:00
display_name = " Get Image Size " ,
description = " Returns width and height of the image, and passes it through unchanged. " ,
category = " image " ,
inputs = [
IO . Image . Input ( " image " ) ,
] ,
outputs = [
IO . Int . Output ( display_name = " width " ) ,
IO . Int . Output ( display_name = " height " ) ,
IO . Int . Output ( display_name = " batch_size " ) ,
] ,
hidden = [ IO . Hidden . unique_id ] ,
)
2025-06-02 18:57:50 -07:00
2025-12-27 05:39:02 +02:00
@classmethod
def execute ( cls , image ) - > IO . NodeOutput :
2025-06-02 18:57:50 -07:00
height = image . shape [ 1 ]
width = image . shape [ 2 ]
2025-06-04 06:40:21 -07:00
batch_size = image . shape [ 0 ]
2025-06-02 18:57:50 -07:00
# Send progress text to display size on the node
2025-12-27 05:39:02 +02:00
if cls . hidden . unique_id :
PromptServer . instance . send_progress_text ( f " width: { width } , height: { height } \n batch size: { batch_size } " , cls . hidden . unique_id )
2025-06-02 18:57:50 -07:00
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( width , height , batch_size )
2025-06-02 18:57:50 -07:00
2025-12-27 05:39:02 +02:00
get_size = execute # TODO: remove
class ImageRotate ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageRotate " ,
2026-05-05 08:37:25 +08:00
display_name = " Rotate Image " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " turn " , " flip orientation " ] ,
2025-12-27 05:39:02 +02:00
category = " image/transform " ,
feat: add essentials_category (#12357)
* feat: add essentials_category field to node schema
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* feat: add ESSENTIALS_CATEGORY to core nodes
Marked nodes:
- Basic: LoadImage, SaveImage, LoadVideo, SaveVideo, Load3D, CLIPTextEncode
- Image Tools: ImageScale, ImageInvert, ImageBatch, ImageCrop, ImageRotate, ImageBlur
- Image Tools/Preprocessing: Canny
- Image Generation: LoraLoader
- Audio: LoadAudio, SaveAudio
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3
* Add ESSENTIALS_CATEGORY to more nodes
- SaveGLB (Basic)
- GetVideoComponents (Video Tools)
- TencentTextToModelNode, TencentImageToModelNode (3D)
- RecraftRemoveBackgroundNode (Image Tools)
- KlingLipSyncAudioToVideoNode (Video Generation)
- OpenAIChatNode (Text Generation)
- StabilityTextToAudio (Audio)
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b69-81c1-71c3-8096-450a39e20910
* fix: correct essentials category for Canny node
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: replace essentials_category string literals with constants
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* refactor: revert constants, use string literals for essentials_category
Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e
Co-authored-by: Amp <amp@ampcode.com>
* fix: update basics
---------
Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-20 11:00:26 +08:00
essentials_category = " Image Tools " ,
2025-12-27 05:39:02 +02:00
inputs = [
IO . Image . Input ( " image " ) ,
IO . Combo . Input ( " rotation " , options = [ " none " , " 90 degrees " , " 180 degrees " , " 270 degrees " ] ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
2025-07-03 16:16:30 -07:00
2025-12-27 05:39:02 +02:00
@classmethod
def execute ( cls , image , rotation ) - > IO . NodeOutput :
2025-07-03 16:16:30 -07:00
rotate_by = 0
if rotation . startswith ( " 90 " ) :
rotate_by = 1
elif rotation . startswith ( " 180 " ) :
rotate_by = 2
elif rotation . startswith ( " 270 " ) :
rotate_by = 3
image = torch . rot90 ( image , k = rotate_by , dims = [ 2 , 1 ] )
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( image )
2025-07-03 16:16:30 -07:00
2025-12-27 05:39:02 +02:00
rotate = execute # TODO: remove
class ImageFlip ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageFlip " ,
add search aliases to all nodes (#12035)
* feat: Add search_aliases field to node schema
Adds `search_aliases` field to improve node discoverability. Users can define alternative search terms for nodes (e.g., "text concat" → StringConcatenate).
Changes:
- Add `search_aliases: list[str]` to V3 Schema
- Add `SEARCH_ALIASES` support for V1 nodes
- Include field in `/object_info` response
- Add aliases to high-priority core nodes
V1 usage:
```python
class MyNode:
SEARCH_ALIASES = ["alt name", "synonym"]
```
V3 usage:
```python
io.Schema(
node_id="MyNode",
search_aliases=["alt name", "synonym"],
...
)
```
## Related PRs
- Frontend: Comfy-Org/ComfyUI_frontend#XXXX (draft - merge after this)
- Docs: Comfy-Org/docs#XXXX (draft - merge after stable)
* Propagate search_aliases through V3 Schema.get_v1_info to NodeInfoV1
* feat: add SEARCH_ALIASES for core nodes (#12016)
Add search aliases to 22 core nodes in nodes.py to improve node discoverability:
- Checkpoint/model loaders: CheckpointLoader, DiffusersLoader
- Conditioning nodes: ConditioningAverage, ConditioningSetArea, ConditioningSetMask, ConditioningZeroOut
- Style nodes: StyleModelApply
- Image nodes: LoadImageMask, LoadImageOutput, ImageBatch, ImageInvert, ImagePadForOutpaint
- Latent nodes: LoadLatent, SaveLatent, LatentBlend, LatentComposite, LatentCrop, LatentFlip, LatentFromBatch, LatentUpscale, LatentUpscaleBy, RepeatLatentBatch
* feat: add SEARCH_ALIASES for image, mask, and string nodes (#12017)
Add search aliases to nodes in comfy_extras for better discoverability:
- nodes_mask.py: mask manipulation nodes
- nodes_images.py: image processing nodes
- nodes_post_processing.py: post-processing effect nodes
- nodes_string.py: string manipulation nodes
- nodes_compositing.py: compositing nodes
- nodes_morphology.py: morphological operation nodes
- nodes_latent.py: latent space nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for audio and video nodes (#12018)
Add search aliases to audio and video nodes for better discoverability:
- nodes_audio.py: audio loading, saving, and processing nodes
- nodes_video.py: video loading and processing nodes
- nodes_wan.py: WAN model nodes
Uses search_aliases parameter in io.Schema() for v3 nodes.
* feat: add SEARCH_ALIASES for model and misc nodes (#12019)
Add search aliases to model-related and miscellaneous nodes:
- Model nodes: nodes_model_merging.py, nodes_model_advanced.py, nodes_lora_extract.py
- Sampler nodes: nodes_custom_sampler.py, nodes_align_your_steps.py
- Control nodes: nodes_controlnet.py, nodes_attention_multiply.py, nodes_hooks.py
- Training nodes: nodes_train.py, nodes_dataset.py
- Utility nodes: nodes_logic.py, nodes_canny.py, nodes_differential_diffusion.py
- Architecture-specific: nodes_sd3.py, nodes_pixart.py, nodes_lumina2.py, nodes_kandinsky5.py, nodes_hidream.py, nodes_fresca.py, nodes_hunyuan3d.py
- Media nodes: nodes_load_3d.py, nodes_webcam.py, nodes_preview_any.py, nodes_wanmove.py
Uses search_aliases parameter in io.Schema() for v3 nodes, SEARCH_ALIASES class attribute for legacy nodes.
2026-01-22 18:36:58 -08:00
search_aliases = [ " mirror " , " reflect " ] ,
2026-05-05 08:37:25 +08:00
display_name = " Flip Image " ,
2025-12-27 05:39:02 +02:00
category = " image/transform " ,
inputs = [
IO . Image . Input ( " image " ) ,
IO . Combo . Input ( " flip_method " , options = [ " x-axis: vertically " , " y-axis: horizontally " ] ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
2025-07-03 16:16:30 -07:00
2025-12-27 05:39:02 +02:00
@classmethod
def execute ( cls , image , flip_method ) - > IO . NodeOutput :
2025-07-03 16:16:30 -07:00
if flip_method . startswith ( " x " ) :
image = torch . flip ( image , dims = [ 1 ] )
elif flip_method . startswith ( " y " ) :
image = torch . flip ( image , dims = [ 2 ] )
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( image )
2025-07-03 16:16:30 -07:00
2025-12-27 05:39:02 +02:00
flip = execute # TODO: remove
2025-09-02 17:05:57 -07:00
2025-12-27 05:39:02 +02:00
class ImageScaleToMaxDimension ( IO . ComfyNode ) :
2025-09-02 17:05:57 -07:00
2025-12-27 05:39:02 +02:00
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageScaleToMaxDimension " ,
2026-05-05 08:37:25 +08:00
display_name = " Scale Image to Max Dimension " ,
2025-12-27 05:39:02 +02:00
category = " image/upscaling " ,
inputs = [
IO . Image . Input ( " image " ) ,
IO . Combo . Input (
" upscale_method " ,
options = [ " area " , " lanczos " , " bilinear " , " nearest-exact " , " bilinear " , " bicubic " ] ,
) ,
IO . Int . Input ( " largest_size " , default = 512 , min = 0 , max = MAX_RESOLUTION , step = 1 ) ,
] ,
outputs = [ IO . Image . Output ( ) ] ,
)
@classmethod
def execute ( cls , image , upscale_method , largest_size ) - > IO . NodeOutput :
2025-09-02 17:05:57 -07:00
height = image . shape [ 1 ]
width = image . shape [ 2 ]
if height > width :
width = round ( ( width / height ) * largest_size )
height = largest_size
elif width > height :
height = round ( ( height / width ) * largest_size )
width = largest_size
else :
height = largest_size
width = largest_size
samples = image . movedim ( - 1 , 1 )
s = comfy . utils . common_upscale ( samples , width , height , upscale_method , " disabled " )
s = s . movedim ( 1 , - 1 )
2025-12-27 05:39:02 +02:00
return IO . NodeOutput ( s )
upscale = execute # TODO: remove
2026-02-23 18:01:17 -08:00
class SplitImageToTileList ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " SplitImageToTileList " ,
category = " image/batch " ,
search_aliases = [ " split image " , " tile image " , " slice image " ] ,
display_name = " Split Image into List of Tiles " ,
description = " Splits an image into a batched list of tiles with a specified overlap. " ,
inputs = [
IO . Image . Input ( " image " ) ,
IO . Int . Input ( " tile_width " , default = 1024 , min = 64 , max = MAX_RESOLUTION ) ,
IO . Int . Input ( " tile_height " , default = 1024 , min = 64 , max = MAX_RESOLUTION ) ,
IO . Int . Input ( " overlap " , default = 128 , min = 0 , max = 4096 ) ,
] ,
outputs = [
IO . Image . Output ( is_output_list = True ) ,
] ,
)
@staticmethod
def get_grid_coords ( width , height , tile_width , tile_height , overlap ) :
coords = [ ]
2026-02-27 17:16:24 -08:00
stride_x = round ( max ( tile_width * 0.25 , tile_width - overlap ) )
2026-05-05 20:29:11 +08:00
stride_y = round ( max ( tile_height * 0.25 , tile_height - overlap ) )
2026-02-23 18:01:17 -08:00
y = 0
while y < height :
x = 0
y_end = min ( y + tile_height , height )
y_start = max ( 0 , y_end - tile_height )
while x < width :
x_end = min ( x + tile_width , width )
x_start = max ( 0 , x_end - tile_width )
coords . append ( ( x_start , y_start , x_end , y_end ) )
if x_end > = width :
break
x + = stride_x
if y_end > = height :
break
y + = stride_y
return coords
@classmethod
def execute ( cls , image , tile_width , tile_height , overlap ) :
b , h , w , c = image . shape
coords = cls . get_grid_coords ( w , h , tile_width , tile_height , overlap )
output_list = [ ]
for ( x_start , y_start , x_end , y_end ) in coords :
tile = image [ : , y_start : y_end , x_start : x_end , : ]
output_list . append ( tile )
return IO . NodeOutput ( output_list )
class ImageMergeTileList ( IO . ComfyNode ) :
@classmethod
def define_schema ( cls ) :
return IO . Schema (
node_id = " ImageMergeTileList " ,
display_name = " Merge List of Tiles to Image " ,
category = " image/batch " ,
search_aliases = [ " split image " , " tile image " , " slice image " ] ,
is_input_list = True ,
inputs = [
IO . Image . Input ( " image_list " ) ,
IO . Int . Input ( " final_width " , default = 1024 , min = 64 , max = 32768 ) ,
IO . Int . Input ( " final_height " , default = 1024 , min = 64 , max = 32768 ) ,
IO . Int . Input ( " overlap " , default = 128 , min = 0 , max = 4096 ) ,
] ,
outputs = [
IO . Image . Output ( is_output_list = False ) ,
] ,
)
@classmethod
def execute ( cls , image_list , final_width , final_height , overlap ) :
w = final_width [ 0 ]
h = final_height [ 0 ]
ovlp = overlap [ 0 ]
feather_str = 1.0
first_tile = image_list [ 0 ]
b , t_h , t_w , c = first_tile . shape
device = first_tile . device
dtype = first_tile . dtype
2026-02-27 17:16:24 -08:00
coords = SplitImageToTileList . get_grid_coords ( w , h , t_w , t_h , ovlp )
2026-02-23 18:01:17 -08:00
canvas = torch . zeros ( ( b , h , w , c ) , device = device , dtype = dtype )
weights = torch . zeros ( ( b , h , w , 1 ) , device = device , dtype = dtype )
if ovlp > 0 :
y_w = torch . sin ( math . pi * torch . linspace ( 0 , 1 , t_h , device = device , dtype = dtype ) )
x_w = torch . sin ( math . pi * torch . linspace ( 0 , 1 , t_w , device = device , dtype = dtype ) )
y_w = torch . clamp ( y_w , min = 1e-5 )
x_w = torch . clamp ( x_w , min = 1e-5 )
sine_mask = ( y_w . unsqueeze ( 1 ) * x_w . unsqueeze ( 0 ) ) . unsqueeze ( 0 ) . unsqueeze ( - 1 )
flat_mask = torch . ones_like ( sine_mask )
weight_mask = torch . lerp ( flat_mask , sine_mask , feather_str )
else :
weight_mask = torch . ones ( ( 1 , t_h , t_w , 1 ) , device = device , dtype = dtype )
for i , ( x_start , y_start , x_end , y_end ) in enumerate ( coords ) :
if i > = len ( image_list ) :
break
tile = image_list [ i ]
region_h = y_end - y_start
region_w = x_end - x_start
real_h = min ( region_h , tile . shape [ 1 ] )
real_w = min ( region_w , tile . shape [ 2 ] )
y_end_actual = y_start + real_h
x_end_actual = x_start + real_w
tile_crop = tile [ : , : real_h , : real_w , : ]
mask_crop = weight_mask [ : , : real_h , : real_w , : ]
canvas [ : , y_start : y_end_actual , x_start : x_end_actual , : ] + = tile_crop * mask_crop
weights [ : , y_start : y_end_actual , x_start : x_end_actual , : ] + = mask_crop
weights [ weights == 0 ] = 1.0
merged_image = canvas / weights
return IO . NodeOutput ( merged_image )
2025-12-27 05:39:02 +02:00
class ImagesExtension ( ComfyExtension ) :
@override
async def get_node_list ( self ) - > list [ type [ IO . ComfyNode ] ] :
return [
ImageCrop ,
2026-02-17 20:13:39 -05:00
ImageCropV2 ,
BoundingBox ,
2025-12-27 05:39:02 +02:00
RepeatImageBatch ,
ImageFromBatch ,
ImageAddNoise ,
SaveAnimatedWEBP ,
SaveAnimatedPNG ,
SaveSVGNode ,
ImageStitch ,
ResizeAndPadImage ,
GetImageSize ,
ImageRotate ,
ImageFlip ,
ImageScaleToMaxDimension ,
2026-02-23 18:01:17 -08:00
SplitImageToTileList ,
ImageMergeTileList ,
2025-12-27 05:39:02 +02:00
]
async def comfy_entrypoint ( ) - > ImagesExtension :
return ImagesExtension ( )