init wiki (#2)

* init the wiki (tutorials, node references)
* add simple nodes: ConvertToAny, ResolutionToDims
This commit is contained in:
Bug, Ltd.
2025-01-17 18:42:20 +01:00
committed by GitHub
parent 17380c173f
commit 023fe8ab4e
96 changed files with 10232 additions and 13 deletions
+19
View File
@@ -63,6 +63,7 @@ const customLabels = {
output: {
current_page: 'current page',
total_pages: 'total pages',
complete: 'complete?',
},
},
ListFromMultiline: {
@@ -163,6 +164,16 @@ const customLabels = {
mask_precision: 'mask method',
},
},
ConvertToAny: {
output: {
value_any: 'value (Any)',
},
},
ResolutionToDims: {
widget: {
scale_factor: 'scale factor',
},
},
};
const customLooks = {
XYPlotQueue: {
@@ -189,6 +200,14 @@ const customLooks = {
},
},
},
XYPlotDataSplit: {
input: {
xy_plot_data: {
color_off: '#52395B',
color_on: '#9521c3',
},
},
},
ListFromString: {
output: {
list: { shape: 'grid' },
+4 -2
View File
@@ -10,7 +10,7 @@
"List: from Elements (lab)": "Create a list from any number of individual elements",
"List: Merge (lab)": "Merge any number of lists into one",
"List: Limit (lab)": "Limit list to a given size",
"Output Config: Load (local) (lab)": "Load a local JSON or YAML config file, and dynamically generate outputs",
"Output Config: Load (lab)": "Load a local JSON or YAML config file, and dynamically generate outputs",
"Output Config: Retrieve (backend) (lab)": "Retrieve a JSON or YAML config file from backend, and dynamically generate outputs",
"Generic Queue (lab)": "Loop through all values of input list, and send them to output",
"File Queue (lab)": "Loop through all files in folder matching the pattern(s)",
@@ -25,5 +25,7 @@
"Format: Multiline (lab)": "Format a multiline string, with any number of inputs",
"Load Image (RGBA) (lab)": "Load an image as RGBA, with 2 different available methods to generate the mask",
"Save Text File (lab)": "Save text content to a file",
"Sleep (lab)": "Delay execution for the given amount of seconds"
"Sleep (lab)": "Delay execution for the given amount of seconds",
"Convert to Any (lab)": "Convert any value to type Any ('*)",
"Resolution to Dimensions (lab)": "Split a resolution (as string, ex. '1024x1024') to dimensions, optionally multiplied by scale factor"
}
+1 -1
View File
@@ -2,7 +2,7 @@ from ..collection.register_nodes import register_node
from ..shared.utils import AnyReturnTypes
@register_node('Output Config: Load (local)', 'config')
@register_node('Output Config: Load', 'config')
class OutputConfigLocal:
def __init__(self):
pass
+8 -8
View File
@@ -73,12 +73,12 @@ class XYPlotQueue:
}
FUNCTION = 'run'
RETURN_TYPES = (ANY_TYPE, ANY_TYPE, 'XY_PLOT_DATA')
RETURN_NAMES = ('dim1_value', 'dim2_value', 'xy_plot_data')
RETURN_TYPES = ('XY_PLOT_DATA', ANY_TYPE, ANY_TYPE)
RETURN_NAMES = ('xy_plot_data', 'dim1_value', 'dim2_value')
OUTPUT_TOOLTIPS = (
TOOLTIP_XY_PLOT_DATA,
'dim1 value (not typed)',
'dim2 value (not typed)',
TOOLTIP_XY_PLOT_DATA,
)
DESCRIPTION = 'Loop through all values of dim1, optionally combined with dim2 values, and send them to outputs.\nIMPORTANT: for a given dim1 value, all dim2 values are first iterated, before going to the next dim1 value.\nSo it is advised to associate slow operations (e.g. loading checkpoints) to dim1, to ensure better performance.'
@@ -169,7 +169,7 @@ class XYPlotQueue:
)
return {
'result': (values[0], values[1], xy_plot_data),
'result': (xy_plot_data, values[0], values[1]),
'ui': {'index': [index], 'total': [total]},
}
@@ -188,15 +188,15 @@ class XYPlotRender:
'dim1_header_format': (
'STRING',
{
'default': 'dim1: {dim1}',
'tooltip': "temoplate text to be displayed as dim1 header.\nthe '{dim1'} placeholder will be replaced by the current value.\nUse '\\n' for multiline text.",
'default': '{dim1}',
'tooltip': "template text to be displayed as dim1 header.\nthe '{dim1'} placeholder will be replaced by the current value.\nUse '\\n' for multiline text.",
},
),
'dim2_header_format': (
'STRING',
{
'default': 'dim2: {dim2}',
'tooltip': "temoplate text to be displayed as dim2 header.\nthe '{dim2'} placeholder will be replaced by the current value.\nUse '\\n' for multiline text.",
'default': '{dim2}',
'tooltip': "template text to be displayed as dim2 header.\nthe '{dim2'} placeholder will be replaced by the current value.\nUse '\\n' for multiline text.",
},
),
'direction': (
+74
View File
@@ -4,6 +4,7 @@ import os
from PIL import Image
import numpy as np
import torch # type: ignore
import re
import folder_paths # type: ignore
@@ -265,3 +266,76 @@ class Sleep:
def run(self, input: Any, seconds: float):
time.sleep(seconds)
return (input,)
@register_node('Convert to Any', 'utils')
class ConvertToAny:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {
'required': {
'value': (
ANY_TYPE,
{
'label': 'input value',
'tooltip': 'input value to convert to type Any',
},
)
}
}
FUNCTION = 'run'
RETURN_TYPES = (ANY_TYPE,)
RETURN_NAMES = ('value_any',)
OUTPUT_TOOLTIPS = ("input value, converted to type Any ('*')",)
DESCRIPTION = "Convert any value to type Any ('*).\nUseful to pipe to a combo widget, for example."
def run(self, value: Any):
return (value,)
@register_node('Resolution to Dimensions', 'utils')
class ResolutionToDims:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {
'required': {
'resolution': (
'STRING',
{
'default': '1024x1024',
'tooltip': "resolution as string (ex. '1024x1024')",
},
),
'scale_factor': (
'FLOAT',
{
'default': 1,
'step': 0.1,
'display': 'float',
'tooltip': 'dimension multiplier',
},
),
}
}
FUNCTION = 'run'
RETURN_TYPES = ('INT', 'INT')
RETURN_NAMES = ('width', 'height')
OUTPUT_TOOLTIPS = ('width', 'height')
DESCRIPTION = "Split a resolution (as string, ex. '1024x1024') to dimensions, optionally multiplied by scale factor."
def run(self, resolution: str, scale_factor: float):
m = re.search(r'^\s*(\d+)\s*x\s*(\d+)\s*$', resolution)
if not m:
raise ValueError('Invalid resolution')
return (
int(int(m.group(1)) * scale_factor),
int(int(m.group(2)) * scale_factor),
)
+6 -2
View File
@@ -44,8 +44,12 @@ class Grid:
for r, row in enumerate(image_matrix):
for c, image in enumerate(row):
x = c * (self.max_cell_size[0] + self.config_grid.gap)
y = r * (self.max_cell_size[1] + self.config_grid.gap)
x = c * (self.max_cell_size[0] + self.config_grid.gap) + int(
(self.max_cell_size[0] - image.size[0]) / 2
)
y = r * (self.max_cell_size[1] + self.config_grid.gap) + int(
(self.max_cell_size[1] - image.size[1]) / 2
)
grid_image.paste(image, (x, y))
# add headers; if header format for dim1/dim2 is empty, they will be silently ignored
+19
View File
@@ -87,6 +87,7 @@ const customLabels = {
output: {
current_page: 'current page',
total_pages: 'total pages',
complete: 'complete?',
},
},
// LIST
@@ -192,6 +193,16 @@ const customLabels = {
mask_precision: 'mask method',
},
},
ConvertToAny: {
output: {
value_any: 'value (Any)',
},
},
ResolutionToDims: {
widget: {
scale_factor: 'scale factor',
},
},
} as const as Record<string, { [key in Scope]?: Record<string, string> }>
const customLooks = {
@@ -220,6 +231,14 @@ const customLooks = {
},
},
},
XYPlotDataSplit: {
input: {
xy_plot_data: {
color_off: '#52395B',
color_on: '#9521c3',
},
},
},
// LIST
ListFromString: {
output: {
+37
View File
@@ -0,0 +1,37 @@
# ComfyLab-Pack wiki
Welcome to the wiki of the ComfyLab-Pack!
Here you will find plenty of information / tricks to help you discover the nodes, and use them efficiently.
Please take the time to read the pages if you face difficulties, and before [creating an issue](https://github.com/bugltd/ComfyLab-Pack/issues).
Apart from these pages, I also customized most of the labels and tooltips.\
You can get **useful contextual information in tooltips**, by just hovering (putting the mouse pointer) over the node title, or inputs / widgets / outputs. It's not perfect, but should be enough in the most standard cases.
> If you can, do not hesitate to propose a Pull Request to improve this wiki, and benefit to the community:\
> English is not my native language, so there is certainly a lot to do to improve it.
That being said, have a good read, dear friends!
---
**[Node reference](./node%20reference/README.md)**:
- [Output Config (Load / Retrieve)](./node%20reference/output%20config.md)
- [Format (String / Multiline)](./node%20reference/format.md)
- [XY Plot](./node%20reference/xy%20plot/)
- [00 - Core concepts](./node%20reference/xy%20plot/00%20-%20core%20concepts.md)
---
**[Tutorials](./tutorials/)**:
- [OutputConfig](./tutorials/Output%20Config/)
- [01 - simple (value & label)](<./tutorials/Output%20Config/01%20-%20simple%20(value%20and%20label)/>)
- [02 - more options](./tutorials/Output%20Config/02%20-%20more%20options/)
- [XY Plot](./tutorials/XY%20Plot/)
- [01 - the basics](./tutorials/XY%20Plot/01%20-%20the%20basics/)
- [02 - pimp my grid](./tutorials/XY%20Plot/02%20-%20pimp%20my%20grid/)
- [03 - advanced techniques](./tutorials/XY%20Plot/03%20-%20advanced%20techniques/)
- [Format](./tutorials/Format/)
+9
View File
@@ -0,0 +1,9 @@
# Node reference
**[Output Config (Load / Retrieve)](./output%20config.md)**
**[XY Plot](./xy%20plot/)**:
- [00 - Core concepts](./xy%20plot/00%20-%20core%20concepts.md)
**[Format (String / Multiline)](./format.md)**
+58
View File
@@ -0,0 +1,58 @@
# Node reference: Format (String / Multiline)
**[>> Link to the tutorials](../tutorials/Format/)**
## Overview
The `Format` nodes are very simple yet powerful nodes, that are based on the **standard Python string `format()` method**.\
They exist in 2 flavors: `String` or `Multiline`.
They are useful to create a string from a template, and **any number of inputs**.\
Inputs can be of the following ConfyUI types: `STRING`, `INT`, `FLOAT`, `BOOLEAN`.
## How to use
Here we will just cover the most basic usage, but please note that Python string `format()` allows to do very interesting things, beyond the standard string replacement: padding, truncating, ...
### Simple usage
The node takes any number of inputs, that are automatically incremented: `arg0`, `arg1`, ...
Given the following template: `first arg by name: {arg0}, second by index: {1}`, and 2 input string (`tata` and `toto`), we will get the resulting string: `first arg by name: tata, second by index: toto`.
Here we see that **we can reference the inputs either by their name (`argN`), or by their index (starting at 0)**.\
Straightforward, isn't it?
This way, it's **very simple to concatenate 2 strings, append text to a string, ... many operations in one single node!**
And last but not least: at stated above, it can take **other types than strings: integer, float, boolean** (and surely others).
### Going further
For more details and advanced techniques, you can check:
- [the tutorials of the `Format` nodes](../tutorials/Format/)
- [a guide with different possible techniques (padding, ...)](https://pyformat.info/)
- [the official page on python.org](https://docs.python.org/3/tutorial/inputoutput.html#the-string-format-method)
## Inputs / Widgets / Outputs
### Inputs (unlimited)
| input name | type | description | comment |
| :--------: | :-------------------------: | :---------: | :--------------------------------- |
| arg0 | STRING, INT, FLOAT, BOOLEAN | input value | can also be referenced by index: 0 |
| ... | | | |
| argN | STRING, INT, FLOAT, BOOLEAN | input value | can also be referenced by index: N |
### Widgets
| widget name | type | description | comment |
| :---------: | :----: | :-------------: | :------ |
| format | STRING | template string | |
### Outputs
| output name | type | description | comment |
| :---------: | :----: | :------------------------: | :------ |
| formatted | STRING | result string after format | |
+79
View File
@@ -0,0 +1,79 @@
# Node reference: Output Config
**[>> Link to the tutorials](../tutorials/Output%20Config/)**
## Overview
The `Output Config: Load` (and its backend counterpart, `Output Config: Retrieve`) is very different from other nodes: it allows you to load config files and **dynamically create any number of custom outputs**, that you can later connect to other nodes before executing the workflow.
It opens a whole range of use cases: whether you need to standardize tests (LoRA, ...), store a collection of usual use cases, or just share your configs with others, this node can certainly prove very useful.\
**You can define any number of outputs in it, to match your specific needs**.
A config file is a simple text file in one of the following formats: **JSON, JSON5 or YAML** (and also the unofficial JSONC, aka 'json with comments').
A config can be very simple:
```jsonc
{
"seed": 618896223111156, // value can be set directly
"cfg": {
"value": 7.5, // or inside a dictionary, with the 'value' key
},
"steps": 40,
// add all the outputs you need, name them as you wish
}
```
But it can be extended, **to customize the label, color, shape, and even the type of the output**.\
Here a more advanced example (written in YAML):
```yaml
seed:
value: 618896223111156
shape: box
color_off: '#FF0000' # when not connected
color_on: '#00FF00' # when connected
cfg:
value: 7.5
shape: circle # default shape
color_off: blue # we can set colors by name
steps:
value: 40
shape: arrow
color_off: '#dcb21a'
prompt:
value: illuminated pirate ship sailing on a sea with a galaxy in the sky
label: a basic prompt but a custom label
shape: grid
color_off: '#28cfcd'
checkpoint:
value: sd_xl_base_1.0.safetensors
type: '*' # '*' means any type, use it as a last resort
```
## Load vs Retrieve: which one should I choose?
The `Output Config: Load` and `Output Config: Retrieve` are very similar.\
The only difference is that the latter allows you to get a file located in the backend machine, while the former is to load a file where the browser is running.
**If your frontend (browser) is running on the same machine as the backend (as probably many of us), you certainly should choose the `Load` version**: it is more interactive and easier to use.\
The `Retrieve` version is probably only useful in some rare cases, but it's here if needed.
## Widgets
### Output Config: Load
| widget label | type | description | comment |
| :----------------: | :----: | :---------------------------------------------------: | :------------------------------------------------------------------------------------------------ |
| Load output config | button | get and validate the file<br/>then create the outputs | |
| display errors | combo | how to display errors | available options:<br/>- in browser console (F12)<br/>- in a popup (default)<br/>- do not display |
| show config | toggle | show / hide the file preview | |
### Output Config: Retrieve (backend)
| widget label | type | description | comment |
| :-------------------------: | :----: | :---------------------------------------------------: | :------------------------------------------------------------------------------------------------ |
| JSON or YAML file (backend) | text | full path to the config file<br/>in backend machine | |
| Retrieve output config | button | get and validate the file<br/>then create the outputs | |
| display errors | combo | how to display errors | available options:<br/>- in browser console (F12)<br/>- in a popup (default)<br/>- do not display |
| show config | toggle | show / hide the file preview | |
@@ -0,0 +1,73 @@
# Node reference / XY Plot: 00 - Core concepts
As a minimum, to use the `XY Plot`, you need to integrate 2 nodes, `XY Plot: Queue` and `XY Plot: Render`: the first one will organize the queue, the second one will render the grid(s).
![standard nodes](./images/nodes.jpg)
They must be connected by the specific `XY plot data` link : that is the way the queue can inform the render node about the current processing state.
What is processed between them (from `dim1 value` / `dim2 value` outputs of `Queue` to `image` input in `Render`) is for you to choose: as such, **the ComfyLab's XY Plot approach is totally process-independent**.
## XY Plot: Queue
As you can see in the screenshot, there is no concept of row / column or X / Y here, as in many other plot / grid implementations:
- instead, we talk about `dim1` and `dim2` (dimensions 1 & 2)
- this is intended, to improve performance: but we'll talk about that later
- just remember this rule: **for a given `dim1` value, we process all values of `dim2` before switching to the next `dim1` value**
- whether `dim1` and `dim2` are rows or columns is configured later, in the `Render` node (`direction` toggle)
- `dim1` and `dim2` (optional) **take lists as inputs**
- in ComfyUI, a LIST is a LIST, but we cannot know if it's a list of strings, numbers, images, ...
- so another important rule: **what you send is what you get**
- said differently: if you send a list of numbers, you will get number values in output
- or one could say: sh\*t in, sh\*t out...
- for the reason above, the `Queue` node cannot determine what it will get in input, so the type of the outputs is set to `"*"` (Any)
- this makes the `dim1 value` and `dim2 value` outputs very flexible, as you can basically connect them anywhere
- but it comes at a price: **you must ensure that the data in input correspond to the expected data in output**
- for example: if you connect `dim1 value` to the `cfg` input of `KSampler`, you must ensure that the input list is a list of floats. If you send strings, you will obviously get an error when running the workflow
- one of the main features of the `Queue` node is **auto-queuing**
- no need to reset a counter, or manually increase the batch size
- **the `Queue` node will take care of that for you**
- in fact, you should definitely **not set the batch size to anything else than `1`**
## XY Plot: Render
The `Render` node, in its default configuration is easier to understand:
- it takes an `image` as input, for example the result of the `KSampler` processing
- <ins>important</ins>: you are not limited to KSampler
- in fact **you can do what you want between the `Queue` node and the `Render` one**
- it could be loading the image from a folder, doing some image transformations... you decide
- it has 2 outputs:
- `image` is the single image, as received in input
- `grid` is the generated grid, when it has received enough images
- obviously, you can generate multiple grids, if you configure pagination in the `Queue` node
## dim1 vs dim2, which one should I choose? (or the Performance Question)
As seen above, connecting an input list on either `dim1` or `dim2` doesn't determine if data will be displayed as rows or columns: you can simply switch the `direction` in the `Render` node.
So, what does it change?\
In the most simple cases, for example if you plot `CFG` vs `seed`, it should not have any visible impact.
But now, imagine a more advanced case: I want to plot a given list of 3 seeds vs a list of 3 checkoints with the default `KSampler` workflow.\
Let's recall the rule above: **for a given `dim1` value, we process all values of `dim2` before switching to the next `dim1` value**.\
That means that, if I set the list of seeds in `dim1`, the process will work as follows:
- get seed #1, get checkpoint #1 > <ins>load checkpoint #1</ins> > render image
- **keep** seed #1, get checkpoint #2 > <ins>load checkpoint #2</ins> > render image
- **keep** seed #1, get checkpoint #3 > <ins>load checkpoint #3</ins> > render image
- get seed #2, get checkpoint #1 > <ins>load checkpoint #1</ins> > render image
- ...
Loading a checkpoint is a costly operation, in terms of performance: we don't want to load a new checkpoint at each and every iteration.\
So let's switch, connecting the list of checkpoints to `dim1`, and the list of seeds to `dim2`:
- get checkpoint #1, get seed #1 > <ins>load checkpoint #1</ins> > render image
- **keep** checkpoint #1, get seed #2 > _do not load checkpoint #1_ (it's in memory) > render image
- **keep** checkpoint #1, get seed #3 > _do not load checkpoint #1_ (it's in memory) > render image
- ...
By doing so, we optimize the performance as much as possible.\
So as a general rule: **if you need to vary models (checkpoint, LoRA, ...), ensure these values are connected to `dim1`**.\
(And if you vary 2 lists of models, obviously choose the slowest as `dim1`).
@@ -0,0 +1,21 @@
# Node reference / XY Plot: 01 - Queue and Render
**Work in Progress**
## Node overview
![standard nodes](./images/nodes.jpg)
### XY Plot: Render
As per the widgets:
- `dim1: header format` and `dim2: header format`
- enter here a template string, following the syntax of Python string `format()` method
- the `{dim1}` and `{dim2}` placeholders will be replaced by the current dim1 / dim2 value
- very handy if you want to prefix the row / column headers, for example:
- if you have CFG values in dim1, by default the headers will be `7.5`, `8`, ...
- just change the dim1 header format to `CFG: {dim1}` and you will get: `CFG: 7.5`, `CFG: 8`, ...
- there are plenty of advanced techniques, but you will probably not use them here
- however if you want to learn more, you can check the [`Format` node reference](../format.md)
- `direction`: whether dim1 values are displayed as rows, or columns
@@ -0,0 +1,3 @@
# Node reference / XY Plot: 02 - Config nodes
**Work in Progress**
+3
View File
@@ -0,0 +1,3 @@
# Node reference / XY Plot
[00 - Core concepts](./00%20-%20core%20concepts.md)
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

+3
View File
@@ -0,0 +1,3 @@
# Tutorials / Format
TODO
@@ -0,0 +1,97 @@
# Tutorials / Output Config: 01 - simple (value & label)
In this first tutorial, we cover the core principles of `Output Config: Load` with the good old KSampler workflow, and some basic output configuration.
If you have no time yet to follow it, you can jump to [the conclusion](#tldr--conclusion).
## Part 1 - Let's play
### Init the workflow
- open [wortkflow.json](./wortkflow.json) or [workflow.png](./workflow.png) of current folder, in ComfyUI
- or start with the default KSampler workflow, and just add node `Output Config: Load`
### Create the outputs from config
- choose any of the `config 01 - *` config files in your favorite format: `json`, `json5`, or `yaml`
- `json5` and `yaml` allow comments, standard `json` doesn't
- I recommend `json5` or `yaml` for this reason, especially if you intend to share your configs with others
- you will find some detailed explanations in the json5 / yaml example files provided here
- in `Output Config: Load`, click on `Load output config`, and select the config file
At this point, you should see something like this (example with the `.yaml` config file):
![node detail](./details/result%2001.jpg)
> Note: the label for the 4th output has been customized according to our config
### Connect the dots and run the workflow
Connect each of the outputs:
- `seed` to `KSampler` / seed
- `cfg` to `KSampler` / cfg
- `steps` to `KSampler` / steps
- `a basic prompt but a custom label` to `CLIP: Text Encode` (the positive one obviously)
> if you have difficulties doing it, right-click on destination node and `convert widgets to inputs`
Run the prompt: click the main blue `Queue` buttom.
### Adjust to your liking
Modify the config files, change the prompt, or any value as you wish. Enjoy, there is plenty to do at this stage already!
> You can use any text editor to modify the files. I personally use VSCode, with the `JSON5 KIT` extension (but it's probably overkill for many).
## Part 2 - understanding the config
### Preserving connections (and your expectations)
Now, load any of the `bad config 01 - *` files.
![node detail](./details/result%2002.jpg)
We can see that all connections except `seed` have been dropped. Why? There are 3 different reasons for that:
- `cfg` has been renamed to `cfg2`
- the value for `steps` has been changed from `40` to `"40"`
- note the quotes: the value is now a string, not a number anymore
- the label for `prompt` has been changed
> This is intended, to restrict ambiguity when opening a new file. **There are 3 reasons for a connection to be dropped:**
>
> 1. the output name has changed -> drop
> 2. the value type has changed -> drop: if the destination node expects a number, we cannot send a string
> 3. the label has changed -> drop: if your labelled your output a certain way, it's probably for a reason
### Valid config but wrong value type
Now, reconnect the outputs to the `KSampler` node. **It will work for all but `steps`**. Why? \
The reason is simple: the `steps` input in `KSampler` is designed to accept numbers (integers, actually), which seems totally logical. \
By putting our value in quotes, we made it a string, which is not accepted by KSampler / steps.
> It's highly important to respect the type expected by the destination node inputs / widgets: a number is a number, a string is a string. \
> And there's no way the `Output Config: Load` can magically guess, because it cannot predict where the outputs will be connected.
## TL;DR / Conclusion
That's it for this first part!\
We have covered most of the core principles of the `Output Config: Load` node:
- config files can be either in `JSON`, `JSON5` or `YAML` format
- `JSON5` and `YAML` are probably better if you intend to share your configs with others
- it will allow you to document your config, link to your Civitai account, ...
- you can set virtually any number of outputs, you decide how to structure your use case, and how to connect your outputs to other nodes
- values can be expressed in 2 ways:
- by a simple `<output> = <value>` entry
- or inside a dictionary, with the mandatory `value` key
- labels can be customized, by adding the `label` key
- existing connections are dropped if any of the following is changed: name, type or label
- adjust the output type to the type expected by the destination input
- by default, the output type is deducted from the value: it can be a string, a number (integer and/or float), a boolean, an array (list)... or anything else, you choose
- types can be enforced though, we will cover that in the next tutorial
In the next tutorial, we will see other configuration options (to make your node look like a christmas garland if you wish), and how validation errors are handled.
To jump to it: **[follow the link](../02%20-%20more%20options/)**
@@ -0,0 +1,11 @@
{
"seed": 618896223111156,
"cfg2": {
"value": 7.5
},
"steps": "40",
"prompt": {
"value": "illuminated pirate ship sailing on a sea with a galaxy in the sky",
"label": "a new label"
}
}
@@ -0,0 +1,16 @@
/*
bad config 01 - wrong value type:
Here we set number as string
*/
{
seed: 618896223111156,
// output name changed -> connection dropped
cfg2: {
value: 7.5,
},
steps: '40', // note the quotes -> value is a string, not a number anymore
prompt: {
value: 'illuminated pirate ship sailing on a sea with a galaxy in the sky',
label: 'a new label', // label changed -> connection dropped
},
}
@@ -0,0 +1,10 @@
# bad config 01 - wrong value type:
# Here we set number as string
seed: 618896223111156
cfg2: # output name changed -> connection dropped
value: 7.5
steps: '40' # note the quotes -> value is a string, not a number anymore
prompt:
value: illuminated pirate ship sailing on a sea with a galaxy in the sky
label: a new label # label changed -> connection dropped
@@ -0,0 +1,11 @@
{
"seed": 618896223111156,
"cfg": {
"value": 7.5
},
"steps": 40,
"prompt": {
"value": "illuminated pirate ship sailing on a sea with a galaxy in the sky",
"label": "a basic prompt but a custom label"
}
}
@@ -0,0 +1,19 @@
/*
config 01 - simple:
Here we just set the values. Please note:
- we can use comments (thanks to JSON5)
- values can be set in 2 different ways
- we can also customize labels
*/
{
seed: 618896223111156, // we can specify the value directly
// or we can add more details
cfg: {
value: 7.5, // 'value' is mandatory
},
steps: 40,
prompt: {
value: 'illuminated pirate ship sailing on a sea with a galaxy in the sky',
label: 'a basic prompt but a custom label',
},
}
@@ -0,0 +1,14 @@
# config 01 - simple:
# Here we just set the values. Please note:
# - we can use comments (thanks to YAML)
# - values can be set in 2 different ways
# - we can also customize labels
seed: 618896223111156 # we can specify the value directly
# or we can add more details
cfg:
value: 7.5 # 'value' is mandatory
steps: 40
prompt:
value: 'illuminated pirate ship sailing on a sea with a galaxy in the sky'
label: a basic prompt but a custom label # we can customize the label
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

@@ -0,0 +1,428 @@
{
"last_node_id": 11,
"last_link_id": 10,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
262
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
1209,
188
],
"size": [
210,
46
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 10,
"type": "PreviewImage",
"pos": [
1450,
190
],
"size": [
210,
26
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 10
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 11,
"type": "OutputConfigLocal",
"pos": [
-80,
-30
],
"size": [
430,
420
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {
"Node name for S&R": "OutputConfigLocal"
},
"widgets_values": [
null,
0,
"in a popup",
true,
"",
"",
0,
{
"current": true,
"states": {
"true": {
"size": [
430,
420
],
"members": {
"config": "customtext"
}
},
"false": {
"members": {}
}
}
}
]
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
26,
474
],
"size": [
315,
98
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
10,
8,
0,
10,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8264462809917354,
"offset": [
233.8873162735555,
186.57777046155832
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "d57874f127bd018bf895ced61fe871107d588483"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

@@ -0,0 +1,104 @@
# Tutorials / Output Config: 02 - more options
In [the first tutorial](<../01%20-%20simple%20(value%20and%20label)/>), we have seen how to use and modify a config file to add any number of custom outputs, setting the value and optionally the label.
In this second tutorial, we are going to see more configuration options, and also how validation errors are handled. \
While I don't recommend to customize the color or shape too much, to preserve the standard look of ComfyUI, these options exist in the standard code so they are available for you to play with in your configs. Once again, your config, your choice!
If you have no time yet to follow it, you can jump to [the conclusion](#tldr--conclusion).
## Part 1 - Make it shine
### Init the workflow
We will start with the same workflow as in [the first tutorial](<../01%20-%20simple%20(value%20and%20label)/>), so you can either reuse it or download it from here (it's the same).
### Let's make our eyes bleed (a little)
Load any of the `config 01 - *` files into `Output Config: Load`.
![node detail](./details/result%2001.jpg)
That's a bit ugly, isn't it?! Try to connect / disconnect `seed`, to make the color change... isn't it beautiful (or not)?
The available options are:
- `shape`: either 'box', 'circle', 'arrow' or 'grid'
- 'circle' is the default shape
- `color_off` / `color_on`: color when disconnected / connected
- color values can be expressed in the traditional RGB hex way (`#dcb21a`), or by name (`blue`, `red`, ...)
Another option is available, `type`: it allows you to enforce the output type to any of the valid types, as described in [the official docs](https://docs.comfy.org/custom-nodes/custom_node_datatypes), including any custom type (like `CHEESE`). \
It is probably only useful for quite advanced and specific use cases. But please note it exists, if you need it.
However, we are going to make use of it, in a kind of 'hacky' way, just below.
## Part 2 - Any is the (magic and risky) key
### That won't work...
Load any of the `bad config 01 - *` files into `Output Config: Load`.
We have added the `checkpoint` and `sampler` outputs. Please adjust the value for `checkpoint` to your env, if SDXL is not available in your ComfyUI instance.
Now, try to connect them, to `Load Checkpoint` / ckpt_name and `KSampler` / sampler_name respectively. You can't. Why?
These 2 widgets expect a string, but not any string: a string that exists in the list.\
So defining our output as string, as it is implied by the quoted value, is not sufficient.
Let's see how we can remediate this.
### ... unless we play a bit
This time, load any of the `config 02 - *` files into `Output Config: Load`. Once again, adjust `checkpoint` to a model that exists in your ComfyUI instance.
Try to connect the `checkpoint` and `sampler` outputs, to `Load Checkpoint` / ckpt_name and `KSampler` / sampler_name.
This time it works, and we can run the workflow. Why?
We have enforced typing by setting `type: "*"`, which means "Any" in ComfyUI. By doing this, we bypass the type checks and say "don't worry, it will be fine".\
Indeed it was fine, because we have set values that are valid for these inputs. Otherwise, we would have faced an error during workflow execution.
For this reason, my humble piece of advice: **use `"*"` as a last resort**, and prefer standard types whenever you can.\
And also, obviously: **review and understand your config before applying it**, especially if you downloaded it from someone else. While it cannot do anything harmful (the config is strictly validated), you may encounter unexpected results if you don't.
However, as you can see, in some cases it can open locked doors, and make your config extremely versatile, covering many different use cases. You can apply this to virtually any sort of combo widgets: LoRA, ... as long as you ensure the values are valid.
## Part 3 (final) - validation errors
Last but not least, we need to cover one aspect you will most certainly face: validation errors.
The config must follow strict rules, checked before the outputs are created. For those interested, this validation is performed thanks to a JSON schema, that can be found [here](https://github.com/bugltd/ComfyLab-Pack/blob/doc/src/schema/config.output.schema.json).
To help you identify errors in your config, the `Output Config: Load` node can inform you about what failed.
That is the purpose of the widget: `display errors`. It can take one of these values:
- `in a popup`: will show a dialog in the UI
- `in browser console (F12)`: errors will be listed in the browser DevTools console, accessible by pressing F12 (on Firefox at least)
- `do not display`: explicit
If you want to test, load any of the `bad config 02 - *` files.
Result in popup:\
![node detail](./details/error%2001.jpg)
Result in browser console:\
![node detail](./details/error%2002.jpg)
And, of course, the status at the bottom of the node is updated accordingly:
![node detail](./details/error%2003.jpg)
## TL;DR / Conclusion
With this second tutorial, we have seen:
- the other available options: `shape`, `color_on`, `color_off`, `type`
- `type` is probably not useful is most cases, as the output type is deducted from the value: string, integer / float, boolean, list, ...
- however, sometimes we may need to use `type: "*"`, to bypass the typing checks (for combo widgets in particular)
- detailed validation errors can be displayed, to help you understand what needs to be corrected in your config
That's it! I hope you have found these tutorials interesting: I tried to cover all the important aspects of the `Output Config: Load` node, to give you all the keys to use it efficiently.
As you can see, it is very simple to use, but can be very versatile and useful, if you need to standardize your tests / use cases, share your configs with others, ...
Thank you for reading, now it's your turn to be creative and enjoy your configs!!!
@@ -0,0 +1,17 @@
{
"seed": 618896223111156,
"cfg": {
"value": 7.5
},
"steps": 40,
"prompt": {
"value": "illuminated pirate ship sailing on a sea with a galaxy in the sky",
"label": "a basic prompt but a custom label"
},
"checkpoint": {
"value": "sd_xl_base_1.0.safetensors"
},
"sampler": {
"value": "dpmpp_2m_sde"
}
}
@@ -0,0 +1,21 @@
/*
bad config 01 - that won't work:
Here we add the checkpoint and sampler, in a traditional way (as strings)
*/
{
seed: 618896223111156,
cfg: {
value: 7.5,
},
steps: 40,
prompt: {
value: 'illuminated pirate ship sailing on a sea with a galaxy in the sky',
label: 'a basic prompt but a custom label',
},
checkpoint: {
value: 'sd_xl_base_1.0.safetensors', // full filename, adjust to your env
},
sampler: {
value: 'dpmpp_2m_sde', // one of the available values in 'sampler_name'
},
}
@@ -0,0 +1,14 @@
# bad config 01 - that won't work:
# Here we add the checkpoint and sampler, in a traditional way (as strings)
seed: 618896223111156
cfg:
value: 7.5
steps: 40
prompt:
value: illuminated pirate ship sailing on a sea with a galaxy in the sky
label: a basic prompt but a custom label
checkpoint:
value: sd_xl_base_1.0.safetensors # full filename, adjust to your env
sampler:
value: dpmpp_2m_sde # one of the available values in 'sampler_name'
@@ -0,0 +1,23 @@
{
"seed": 618896223111156,
"cfg": {
"not_value": 7.5,
"shape": "rabbit"
},
"steps": {
"value": 40,
"color_off": 5
},
"prompt": {
"not_value": "illuminated pirate ship sailing on a sea with a galaxy in the sky",
"label": "a basic prompt but a custom label"
},
"checkpoint": {
"value": "sd_xl_base_1.0.safetensors",
"type": 98
},
"sampler": {
"value": "dpmpp_2m_sde",
"label": ["in a list"]
}
}
@@ -0,0 +1,27 @@
/*
bad config 02 - show errors:
Demonstrate the possible validation errors
*/
{
seed: 618896223111156,
cfg: {
not_value: 7.5, // invalid
shape: 'rabbit', // invalid
},
steps: {
value: 40,
color_off: 5, // invalid
},
prompt: {
not_value: 'illuminated pirate ship sailing on a sea with a galaxy in the sky', // invalid
label: 'a basic prompt but a custom label',
},
checkpoint: {
value: 'sd_xl_base_1.0.safetensors',
type: 98, // invalid
},
sampler: {
value: 'dpmpp_2m_sde',
label: ['in a list'], // invalid
},
}
@@ -0,0 +1,20 @@
# bad config 02 - show errors:
# Demonstrate the possible validation errors
seed: 618896223111156
cfg:
not_value: 7.5 # invalid
shape: rabbit # invalid
steps:
value: 40
color_off: 5 # invalid
prompt:
not_value: illuminated pirate ship sailing on a sea with a galaxy in the sky # invalid
label: a basic prompt but a custom label
checkpoint:
value: sd_xl_base_1.0.safetensors
type: 98 # invalid
sampler:
value: dpmpp_2m_sde
label:
- in a list # invalid
@@ -0,0 +1,24 @@
{
"seed": {
"value": 618896223111156,
"shape": "box",
"color_off": "#FF0000",
"color_on": "#00FF00"
},
"cfg": {
"value": 7.5,
"shape": "circle",
"color_off": "blue"
},
"steps": {
"value": 40,
"shape": "arrow",
"color_off": "#dcb21a"
},
"prompt": {
"value": "illuminated pirate ship sailing on a sea with a galaxy in the sky",
"label": "a basic prompt but a custom label",
"shape": "grid",
"color_off": "#28cfcd"
}
}
@@ -0,0 +1,28 @@
/*
config 01 - make it shine:
Here we see the different configuration options
*/
{
seed: {
value: 618896223111156,
shape: 'box',
color_off: '#FF0000', // when not connected
color_on: '#00FF00', // when connected
},
cfg: {
value: 7.5,
shape: 'circle', // default shape
color_off: 'blue', // we can set colors by name
},
steps: {
value: 40,
shape: 'arrow',
color_off: '#dcb21a',
},
prompt: {
value: 'illuminated pirate ship sailing on a sea with a galaxy in the sky',
label: 'a basic prompt but a custom label',
shape: 'grid',
color_off: '#28cfcd',
},
}
@@ -0,0 +1,21 @@
# config 01 - make it shine:
# Here we see the different configuration options
seed:
value: 618896223111156
shape: box
color_off: '#FF0000' # when not connected
color_on: '#00FF00' # when connected
cfg:
value: 7.5
shape: circle # default shape
color_off: blue # we can set colors by name
steps:
value: 40
shape: arrow
color_off: '#dcb21a'
prompt:
value: illuminated pirate ship sailing on a sea with a galaxy in the sky
label: a basic prompt but a custom label
shape: grid
color_off: '#28cfcd'
@@ -0,0 +1,19 @@
{
"seed": 618896223111156,
"cfg": {
"value": 7.5
},
"steps": 40,
"prompt": {
"value": "illuminated pirate ship sailing on a sea with a galaxy in the sky",
"label": "a basic prompt but a custom label"
},
"checkpoint": {
"value": "sd_xl_base_1.0.safetensors",
"type": "*"
},
"sampler": {
"value": "dpmpp_2m_sde",
"type": "*"
}
}
@@ -0,0 +1,23 @@
/*
config 02 - any is the key:
Enforce value type as Any to allow connections
*/
{
seed: 618896223111156,
cfg: {
value: 7.5,
},
steps: 40,
prompt: {
value: 'illuminated pirate ship sailing on a sea with a galaxy in the sky',
label: 'a basic prompt but a custom label',
},
checkpoint: {
value: 'sd_xl_base_1.0.safetensors',
type: '*', // '*' means any type, use it as a last resort
},
sampler: {
value: 'dpmpp_2m_sde',
type: '*', // '*' means any type, use it as a last resort
},
}
@@ -0,0 +1,16 @@
# config 02 - any is the key:
# Enforce value type as Any to allow connections
seed: 618896223111156
cfg:
value: 7.5
steps: 40
prompt:
value: illuminated pirate ship sailing on a sea with a galaxy in the sky
label: a basic prompt but a custom label
checkpoint:
value: sd_xl_base_1.0.safetensors
type: '*' # '*' means any type, use it as a last resort
sampler:
value: dpmpp_2m_sde
type: '*' # '*' means any type, use it as a last resort
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

@@ -0,0 +1,428 @@
{
"last_node_id": 11,
"last_link_id": 10,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
262
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
1209,
188
],
"size": [
210,
46
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 10,
"type": "PreviewImage",
"pos": [
1450,
190
],
"size": [
210,
26
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 10
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 11,
"type": "OutputConfigLocal",
"pos": [
-80,
-30
],
"size": [
430,
420
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {
"Node name for S&R": "OutputConfigLocal"
},
"widgets_values": [
null,
0,
"in a popup",
true,
"",
"",
0,
{
"current": true,
"states": {
"true": {
"size": [
430,
420
],
"members": {
"config": "customtext"
}
},
"false": {
"members": {}
}
}
}
]
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
26,
474
],
"size": [
315,
98
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
10,
8,
0,
10,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8264462809917354,
"offset": [
233.8873162735555,
186.57777046155832
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "d57874f127bd018bf895ced61fe871107d588483"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

+29
View File
@@ -0,0 +1,29 @@
# Tutorials / Output Config
The `Output Config` nodes allow you to load custom config files, to **dynamically create any number of custom outputs**, that you can later connect to other nodes before executing the workflow.
The following tutorials cover all you need to know, from a simple config to a more custom one.\
They will also help you understand how you can solve some issues (checkpoints combo, ...), and use the integrated validation check report.
If needed, you can also check the [node reference](../../node%20reference/output%20config.md).
## Tutorial 01 - simple (value & label)
**[>> Go to the tutorial](<./01%20-%20simple%20(value%20and%20label)/>)**
Contents:
- config file formats
- how to set output name and value, as well as a custom label
- automatic deduction of the value type
## Tutorial 02 - more options
**[>> Go to the tutorial](./02%20-%20more%20options/)**
Contents:
- other customization options
- the `type` option
- how to deal with combo widgets (checkpoint list, ...)
- detailed validation errors to help you correct your config
+14
View File
@@ -0,0 +1,14 @@
# Tutorials
**[Output Config](./Output%20Config/):**
- [01 - simple (value & label)](<./Output%20Config/01%20-%20simple%20(value%20and%20label)/>)
- [02 - more options](./Output%20Config/02%20-%20more%20options/)
**[XY Plot](./XY%20Plot/):**
- [01 - the basics](./XY%20Plot/01%20-%20the%20basics/)
- [02 - pimp my grid](./XY%20Plot/02%20-%20pimp%20my%20grid/)
- [03 - advanced techniques](./XY%20Plot/03%20-%20advanced%20techniques/)
**[Format](./Format/)**
@@ -0,0 +1,144 @@
# Tutorials / XY Plot: 01 - the basics
In this first tutorial, we are going to generate our first grids, using the 2 standard XY Plot nodes.
We will also focus a bit on some useful utility nodes, and play with string templating, pagination, and switch rows / headers as we wish.
Tutorial sections:
- [Part 1 - Our first grid (and some useful explanations)](#part-1---our-first-grid-and-some-useful-explanations)
- [Part 2 - String templating](#part-2---string-templating)
- [Part 3 - rows / columns and pagination](#part-3---rows--columns-and-pagination)
- [TL;DR / Conclusion](#tldr--conclusion)
To keep it simple, we will not detail the core concepts here. But if you are interested, now or later, you can check the [dedicated page](../../../node%20reference/xy%20plot/00%20-%20core%20concepts.md).
> As for all nodes in this extension, you can get useful contextual information, by just **putting the mouse pointer over a node or its inputs / widgets / outputs**. This should help you understand some details, without reading the more detailed wiki pages (yet).
And as always, if you do not want to follow all the steps, you can jump directly to [the conclusion](#tldr--conclusion).
---
## Part 1 - Our first grid (and some useful explanations)
**Load either `workflow - part 1.json` or `workflow - part 1.png` into ComfyUI.**\
**Ensure that the batch size is set to 1 (default)**.\
Adjust the checkpoint to one available in your ComfyUI instance.\
Execute the workflow.
You should get something like this (obviously not exactly the same images):
![result - part 1](./details/result%20-%20part%201.jpg)
What do we see?
- the `image` output of the `Render` node is updated at each KSampler processing
- the `grid` output, however, is only rendered when the whole queue has been processed
### Understanding the config and processing
#### The inputs
The `Queue` node takes lists as inputs. For this we use the nodes `Format: from Multiline` and `Format: from String`, available in the ComfyLab extension.\
Of course, you can choose nodes from other extensions, as long as they provide lists.
> There are other list-related nodes in the ComfyLab collection if you need
Let's focus a bit:
##### prompt input (multiline)
If you look at the first one, you'll see that we haven't generated images of an astronaut. That's because the line starts with a `#`, and the node is set to strip comments.\
![strip comments](./details/detail%20-%20part%201%20-%20comments.jpg)\
Just remove the `#` and you will get an astronaut!
##### CFG input (string with seperator)
![float conversion](./details/detail%20-%20part%201%20-%20float.jpg)\
- we have set the values in a string, separated by `, `: `5.5, 7, 10.5`
- the separator is configured accordingly, you can use any character you want: `/`, `|`, `&`, ...
- and we have configured the conversion: `convert: float`
- that is a very important [core concept](../../../node%20reference/xy%20plot/00%20-%20core%20concepts.md):
- the `Queue` node cannot determine to which input you will connect the dim values
- that is why the output type is `*` (Any): you can connect them anywhere, but **you must ensure the type of input values corresponds to where you will use them**
For the test, just set the `convert` widget to `disabled`. You will get an error on execution: CFG expects a float, and we sent strings.\
![conversion error](./details/detail%20-%20part%201%20-%20convert%20error.jpg)
#### processing / auto-queuing
The `Queue` and `Render` nodes are connected by the specific `XY plot data` link: that is the way the queue updates the render node with the current processing state.
During processing, the `Queue` node informs you about the current state: `Processing: 2 / 9` for example
- you can re-run, you will see that the queue is reinitialized automatically:
- the `Queue` node is designed for **auto-queuing**: you don't have to reset a counter or adjust the batch size, **it takes care of everything for you**
## Part 2 - String templating
Let's do our first customizations, that will be short:
- in the dim1 multiline input, you can see that all lines start with `a photograpgy of`
- that's redundant, and make row headers too long: let's optimize this
- in the grid, the columns just display the CFG values. Let's add a prefix `CFG: `
**Load either `workflow - part 2.json` or `workflow - part 2.png` into ComfyUI**.\
Execute the workflow.
![result - part 2](./details/result%20-%20part%202.jpg)\
Better, isn't it?
### string format to the rescue
What did we change?
First, we have inserted the [`Format: String` node](../../../node%20reference/format.md), between the dim1 output of the queue, and the CLIP Text Encode node.\
![format node](./details/detail%20-%20part%202%20-%20prompt.jpg)\
It is as simple as it is powerful:
- it takes advantage of Python string `format()` method, to allow string replacement, and many other things, by using placeholders: here `{arg0}`, as the name of the input
- this node also takes any number of inputs, even though we have just used one here
- all credits to [@melMass](https://github.com/melMass) and his [comfy_mtb](https://github.com/melMass/comfy_mtb) extension for the dynamic input trick!
- here we just added a prefix and made a simple replacement: `photography of {arg0}`
- for more details about the `Format` nodes, we can read the [node reference](../../../node%20reference/format.md)
We did a very similar thing, this time in the `XY Plot: Render` node:\
![dim2 header](./details/detail%20-%20part%202%20-%20header.jpg)\
- we have set the `dim2: header format` string to `CFG: {dim2}`
- the only difference is the placeholder name, `{dim2}` here
## Part 3 - Rows / columns and pagination
**Load either `workflow - part 3.json` or `workflow - part 3.png` into ComfyUI**.\
Execute the workflow.
What we have changed:
- the astronaut is back into business (we have removed the `#`, see Part 1)
- in the `XY Plot: Queue` node, we have set `dim1: max per page` to 2
- this where we set the pagination (0 to disable it)
- in the `XT Plot: Render` node, the `direction` was changed to `dim1 as cols`
And if you observe the `grid` output of the Render node, we can indeed see that:
- it has generated 2 grids / pages (be quick to see both)
- prompt is now in columns, and CFG in rows
![page 1](./details/result%20-%20part%203%20-%20page%201.jpg)
![page 2](./details/result%20-%20part%203%20-%20page%202.jpg)
## TL;DR / Conclusion
We have covered many important things in this first tutorial:
- we only need 2 nodes to make grids: `Queue` and `Render`
- there are linked with the specific `XY plot data` output / input
- the `Queue` node takes lists as inputs
- although you can connect the outputs anywhere, you must ensure the type of the list values corresponds to the one expected by the input you connect it to
- for example: float for CFG, integer for steps, ...
- you can use string templating (fornat) to customize the row / column headers, or to automatize prefixing
- you can easily activate pagination, and switch rows / columns, in the `Queue` and `Render` nodes respectively
Okay, that's nice, we have created grids, but TBH they have nothing special for the moment...\
**Where ComfyLab's XY Plot really shines is its configurability**, to help you design the grids you want, like you want:\
That's exactly what we are going to see in the **next tutoral: [02 - pimp my grid](../02%20-%20pimp%20my%20grid/)**
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@@ -0,0 +1,708 @@
{
"last_node_id": 15,
"last_link_id": 17,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
262
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
},
{
"name": "cfg",
"type": "FLOAT",
"link": 13,
"widget": {
"name": "cfg"
}
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
930,
550
],
"size": [
210,
46
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
26,
474
],
"size": [
315,
98
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
},
{
"name": "text",
"type": "STRING",
"link": 12,
"widget": {
"name": "text"
}
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 10,
"type": "XYPlotQueue",
"pos": [
30,
100
],
"size": [
315,
146
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "dim1",
"type": "LIST",
"link": 16,
"shape": 6
},
{
"name": "dim2",
"type": "LIST",
"link": 17,
"shape": 6,
"label": "dim2 (opt)"
}
],
"outputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"links": [
11
],
"slot_index": 0,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "dim1_value",
"type": "*",
"links": [
12
],
"slot_index": 1,
"label": "dim1 value"
},
{
"name": "dim2_value",
"type": "*",
"links": [
13
],
"slot_index": 2,
"label": "dim2 value"
}
],
"properties": {
"Node name for S&R": "XYPlotQueue"
},
"widgets_values": [
0,
0,
0
]
},
{
"id": 11,
"type": "XYPlotRender",
"pos": [
1250,
100
],
"size": [
315,
186
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"link": 11,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "image",
"type": "IMAGE",
"link": 10
},
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"link": null,
"shape": 7,
"label": "config: grid (opt)"
},
{
"name": "plot_config_header",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: header (opt)"
},
{
"name": "plot_config_footer",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: footer (opt)"
}
],
"outputs": [
{
"name": "grid",
"type": "IMAGE",
"links": [
14
],
"slot_index": 0
},
{
"name": "image",
"type": "IMAGE",
"links": [
15
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "XYPlotRender"
},
"widgets_values": [
"{dim1}",
"{dim2}",
true
]
},
{
"id": 12,
"type": "PreviewImage",
"pos": [
1300,
360
],
"size": [
210,
26
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 15
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 13,
"type": "PreviewImage",
"pos": [
1620,
100
],
"size": [
210,
26
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 14
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 15,
"type": "ListFromString",
"pos": [
-320,
380
],
"size": [
210,
126
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
17
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromString"
},
"widgets_values": [
"5.5, 7, 10.5",
",",
"float"
]
},
{
"id": 14,
"type": "ListFromMultiline",
"pos": [
-370,
100
],
"size": [
300,
220
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
16
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"photography of a dog in a park\nphotography of a man in a restaurant\n#photography of an astronaut on Mars\nphotography of a woman on a balcony"
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
10,
8,
0,
11,
1,
"IMAGE"
],
[
11,
10,
0,
11,
0,
"XY_PLOT_DATA"
],
[
12,
10,
1,
6,
1,
"STRING"
],
[
13,
10,
2,
3,
4,
"FLOAT"
],
[
14,
11,
0,
13,
0,
"IMAGE"
],
[
15,
11,
1,
12,
0,
"IMAGE"
],
[
16,
14,
0,
10,
0,
"LIST"
],
[
17,
15,
0,
10,
1,
"LIST"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
495,
97
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "e957a7e4d74579f81e3f4457b8d672327c8ac7f7"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

@@ -0,0 +1,760 @@
{
"last_node_id": 16,
"last_link_id": 19,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
262
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
},
{
"name": "cfg",
"type": "FLOAT",
"link": 13,
"widget": {
"name": "cfg"
}
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
930,
550
],
"size": [
210,
46
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
26,
474
],
"size": [
315,
98
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
},
{
"name": "text",
"type": "STRING",
"link": 19,
"widget": {
"name": "text"
}
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 12,
"type": "PreviewImage",
"pos": [
1300,
360
],
"size": [
210,
26
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 15
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 13,
"type": "PreviewImage",
"pos": [
1620,
100
],
"size": [
210,
26
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 14
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 15,
"type": "ListFromString",
"pos": [
-320,
380
],
"size": [
210,
126
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
17
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromString"
},
"widgets_values": [
"5.5, 7, 10.5",
",",
"float"
]
},
{
"id": 10,
"type": "XYPlotQueue",
"pos": [
30,
100
],
"size": [
315,
146
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "dim1",
"type": "LIST",
"link": 16,
"shape": 6
},
{
"name": "dim2",
"type": "LIST",
"link": 17,
"shape": 6,
"label": "dim2 (opt)"
}
],
"outputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"links": [
11
],
"slot_index": 0,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "dim1_value",
"type": "*",
"links": [
18
],
"slot_index": 1,
"label": "dim1 value"
},
{
"name": "dim2_value",
"type": "*",
"links": [
13
],
"slot_index": 2,
"label": "dim2 value"
}
],
"properties": {
"Node name for S&R": "XYPlotQueue"
},
"widgets_values": [
0,
0,
0
]
},
{
"id": 16,
"type": "FormatString",
"pos": [
40,
320
],
"size": [
290,
78
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "arg0",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": 18,
"label": "arg0"
},
{
"name": "arg1",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": null
}
],
"outputs": [
{
"name": "formatted",
"type": "STRING",
"links": [
19
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FormatString"
},
"widgets_values": [
"photography of {arg0}"
]
},
{
"id": 14,
"type": "ListFromMultiline",
"pos": [
-370,
100
],
"size": [
300,
220
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
16
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"a dog in a park\na man in a restaurant\n#an astronaut on Mars\na woman on a balcony"
]
},
{
"id": 11,
"type": "XYPlotRender",
"pos": [
1250,
100
],
"size": [
315,
186
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"link": 11,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "image",
"type": "IMAGE",
"link": 10
},
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"link": null,
"shape": 7,
"label": "config: grid (opt)"
},
{
"name": "plot_config_header",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: header (opt)"
},
{
"name": "plot_config_footer",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: footer (opt)"
}
],
"outputs": [
{
"name": "grid",
"type": "IMAGE",
"links": [
14
],
"slot_index": 0
},
{
"name": "image",
"type": "IMAGE",
"links": [
15
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "XYPlotRender"
},
"widgets_values": [
"{dim1}",
"CFG: {dim2}",
true
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
10,
8,
0,
11,
1,
"IMAGE"
],
[
11,
10,
0,
11,
0,
"XY_PLOT_DATA"
],
[
13,
10,
2,
3,
4,
"FLOAT"
],
[
14,
11,
0,
13,
0,
"IMAGE"
],
[
15,
11,
1,
12,
0,
"IMAGE"
],
[
16,
14,
0,
10,
0,
"LIST"
],
[
17,
15,
0,
10,
1,
"LIST"
],
[
18,
10,
1,
16,
0,
"STRING,INT,FLOAT,BOOLEAN"
],
[
19,
16,
0,
6,
1,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
31,
115
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "e957a7e4d74579f81e3f4457b8d672327c8ac7f7"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

@@ -0,0 +1,760 @@
{
"last_node_id": 16,
"last_link_id": 19,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
262
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
},
{
"name": "cfg",
"type": "FLOAT",
"link": 13,
"widget": {
"name": "cfg"
}
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
930,
550
],
"size": [
210,
46
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
26,
474
],
"size": [
315,
98
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
},
{
"name": "text",
"type": "STRING",
"link": 19,
"widget": {
"name": "text"
}
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 12,
"type": "PreviewImage",
"pos": [
1300,
360
],
"size": [
210,
26
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 15
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 13,
"type": "PreviewImage",
"pos": [
1620,
100
],
"size": [
210,
26
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 14
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 15,
"type": "ListFromString",
"pos": [
-320,
380
],
"size": [
210,
126
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
17
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromString"
},
"widgets_values": [
"5.5, 7, 10.5",
",",
"float"
]
},
{
"id": 16,
"type": "FormatString",
"pos": [
40,
320
],
"size": [
290,
78
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "arg0",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": 18,
"label": "arg0"
},
{
"name": "arg1",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": null
}
],
"outputs": [
{
"name": "formatted",
"type": "STRING",
"links": [
19
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FormatString"
},
"widgets_values": [
"photography of {arg0}"
]
},
{
"id": 14,
"type": "ListFromMultiline",
"pos": [
-370,
100
],
"size": [
300,
220
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
16
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"a dog in a park\na man in a restaurant\nan astronaut on Mars\na woman on a balcony"
]
},
{
"id": 10,
"type": "XYPlotQueue",
"pos": [
30,
100
],
"size": [
315,
146
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "dim1",
"type": "LIST",
"link": 16,
"shape": 6
},
{
"name": "dim2",
"type": "LIST",
"link": 17,
"shape": 6,
"label": "dim2 (opt)"
}
],
"outputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"links": [
11
],
"slot_index": 0,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "dim1_value",
"type": "*",
"links": [
18
],
"slot_index": 1,
"label": "dim1 value"
},
{
"name": "dim2_value",
"type": "*",
"links": [
13
],
"slot_index": 2,
"label": "dim2 value"
}
],
"properties": {
"Node name for S&R": "XYPlotQueue"
},
"widgets_values": [
0,
2,
0
]
},
{
"id": 11,
"type": "XYPlotRender",
"pos": [
1250,
100
],
"size": [
315,
186
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"link": 11,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "image",
"type": "IMAGE",
"link": 10
},
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"link": null,
"shape": 7,
"label": "config: grid (opt)"
},
{
"name": "plot_config_header",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: header (opt)"
},
{
"name": "plot_config_footer",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: footer (opt)"
}
],
"outputs": [
{
"name": "grid",
"type": "IMAGE",
"links": [
14
],
"slot_index": 0
},
{
"name": "image",
"type": "IMAGE",
"links": [
15
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "XYPlotRender"
},
"widgets_values": [
"{dim1}",
"CFG: {dim2}",
false
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
10,
8,
0,
11,
1,
"IMAGE"
],
[
11,
10,
0,
11,
0,
"XY_PLOT_DATA"
],
[
13,
10,
2,
3,
4,
"FLOAT"
],
[
14,
11,
0,
13,
0,
"IMAGE"
],
[
15,
11,
1,
12,
0,
"IMAGE"
],
[
16,
14,
0,
10,
0,
"LIST"
],
[
17,
15,
0,
10,
1,
"LIST"
],
[
18,
10,
1,
16,
0,
"STRING,INT,FLOAT,BOOLEAN"
],
[
19,
16,
0,
6,
1,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
18,
67
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "e957a7e4d74579f81e3f4457b8d672327c8ac7f7"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

@@ -0,0 +1,158 @@
# Tutorials / XY Plot: 02 - pimp my grid
In this second tutorial, we are going to discover the true power of ComfyLab's XY Plot: its configurability.\
First by tweaking the grid itself, then by adding header and footer to the page.\
And finally, by setting a custom background image.
Tutorial sections:
- [Part 1 - Customize the grid](#part-1---customize-the-grid)
- [Part 2 - Add page header / footer](#part-2---add-page-header--footer)
- [Part 3 - Add a background image (RGBA mode)](#part-3---add-a-background-image-rgba-mode)
- [Part 4 - Display 'page X / Y`](#part-4---display-page-x--y)
- [TL;DR / Conclusion](#tldr--conclusion)
- [Going further (advanced)](#going-further-advanced)
> As for all nodes in this extension, you can get useful contextual information, by just **putting the mouse pointer over a node or its inputs / widgets / outputs**. This should help you understand some details, without reading the more detailed wiki pages (yet).
And as always, if you do not want to follow all the steps, you can jump directly to [the conclusion](#tldr--conclusion).
---
## Part 1 - Customize the grid
**Load either `workflow - part 1.json` or `workflow - part 1.png` into ComfyUI.**\
Adjust the checkpoint to one available in your ComfyUI instance.\
Execute the workflow.
![result - part 1](./details/result%20-%20part%201.jpg)
Very different, isn't it?\
We have just added the optional `Plot Config: Grid` node, and adjusted some configuration values:
![grid config](./details/detail%20-%20part%201%20-%20grid.jpg)
In details:
- increased `gap`
- set `background color` and `font color`
- changed the `font` and `font size`. Font can be:
- either one of the fonts shipped in the extension: _Roboto-Regular.ttf_ / _Roboto-Bold.ttf_ / _Roboto-Italic.ttf_ / _Roboto-BoldItalic.ttf_,
- or the full path to a locally-installed TTF font
- this way you can make your grid really different
- `padding`: you can adjust the space surrounding the row / col headers
- `wrap`: wrap the row / col headers to a given number of characters, if they are too long
With just this simple optional node, we have already completely changed the look of our grid.\
But we can do more: let's add some page header / footer, shall we?
## Part 2 - Add page header / footer
**Load either `workflow - part 2.json` or `workflow - part 2.png` into ComfyUI.**\
Adjust the checkpoint to one available in your ComfyUI instance.\
Execute the workflow.
![result - part 2](./details/result%20-%20part%202.jpg)
Not the most aesthetic I admit... but that's just for the demonstration.\
We have added the `Plot Config: Header/Footer` node twice, one for the header and one for the footer.\
This way **you can configure them separately, or just add a footer for example**.\
![header](./details/detail%20-%20part%202%20-%20header.jpg)
![footer](./details/detail%20-%20part%202%20-%20footer.jpg)
Some of the configuration options are similar to the `Plot Config: Grid` (font, ...), and some are new:
- `text`: you add text, and position it either at left / center / right
- of course you can add multiple texts, as we have done in the footer
- `background color`: you can set a color (as in header), or use the special value `transparent` (as in footer) to keep the grid bg color
- `padding`: add vertical space above and below the header / footer text
> a note about colors (either font or background, in all the `Plot Config` nodes):\
> as you can see in the footer, you can specify them by name (eg `white`), instead of the traditional RGB hex notation
With just these 3 `Plot Config` nodes, we now have access to a bunch of configuration options.\
But there's maybe something more we can do: what about adding a custom background image?
## Part 3 - Add a background image (RGBA mode)
**Load either `workflow - part 3.json` or `workflow - part 3.png` into ComfyUI.**\
Adjust the checkpoint to one available in your ComfyUI instance.\
Upload a background image into the `Load Image` node. You can find 2 samples in the `backgrounds` subfolder, feel free to use your own.\
Execute the workflow.
![result - part 3](./details/result%20-%20part%203.jpg)
What did we do?
First, in the `Plot Config: Grid` node, we set the `background color` to the special value `transparent`:
![transparent background](./details/detail%20-%20part%203%20-%20background.jpg)
Then, we pipe the `grid` output from `XY Plot: Render` to the standard node `Split Image with Alpha`, then IMAGE and MASK to `ImageCompositeMasked`:
![RGBA pipe](./details/detail%20-%20part%203%20-%20rgba.jpg)
Finally, we add a `Load Image` node to load the background, and connect it to `source` in `ImageCompositeMasked`.
All that with ComfyLab and a few Comfy Core nodes. That wasn't too difficult, don't you think?
## Part 4 - Display 'page X / Y'
**Load either `workflow - part 4.json` or `workflow - part 4.png` into ComfyUI.**\
Adjust the checkpoint to one available in your ComfyUI instance.\
Upload a background image into the `Load Image` node. You can find 2 samples in the `backgrounds` subfolder, feel free to use your own.\
Execute the workflow.
![result - part 4 page 1](./details/result%20-%20part%204%20-%20page%201.jpg)
![result - part 4 page 2](./details/result%20-%20part%204%20-%20page%202.jpg)
What did we do?\
![nodes](./details/detail%20-%20part%204%20-%20nodes.jpg)
We have simply added the `XY Plot: Split Data` node, then used a `Format: String` to build the text from `current page` and `total pages`, then finally pipe the formatted string into `text (right)` of the `Render` node:
> We have used `XY Plot: Split Data` to adjust the display, but of course it can be useful for other purposes, like naming the grid image file when saving it.
## TL;DR / Conclusion
In just 4 steps and 3 optional nodes, we have totally changed the look of our grid.
What we have covered:
- use the `Plot Config: Grid` to customize the grid:
- cell gap, background / font color, font size, col / row padding, col / row wrap
- you can use either one the fonts shipped with this extension, or use your own TTF one
- add page header / footer with the `Plot Config: Header/Footer` node
- you can configure them separately, or just add a footer if you want
- with custom text, either at left / center / right (or all of them)
- set a custom background color, or use the grid one
- custom font and padding
- make the grid RGBA, to add a background image thanks to a few standard nodes
- use `XY Plot: Split Data` to display 'page X / Y' in the footer
With these 2 tutorials, **we have seen how to create grids, and make them unique**.\
It's now your turn to use your creativity and aesthetic talent (surely better than mine), to make them as beautiful as you wish!
That being said, I hope you have found these tutorials interesting and easy to follow.\
If you have any suggestion to improve them (content, features, better English, shorter sentences, ...), feel free to open [an issue](https://github.com/bugltd/ComfyLab-Pack/issues), [a discussion](https://github.com/bugltd/ComfyLab-Pack/discussions), or even a Pull Request if you can.
Thank you for reading, and enjoy your beautiful grids my friends!
## Going further (advanced)
These 2 tutorials are intended for a large audience, and to cover the most standard use cases.\
But please note there are **many advanced techniques you can apply with ComfyLab XY Plot nodes**, for example:
- vary checkpoints, LoRA (any model in fact), ... virtually anything
- automatize LoRA testing: different epochs, weight, ...
- use a totally different process: load images from multiple folders, apply transformations... it's totally flexible
- use various image resolutions / aspect ratios
- use a JSON / JSON5 / YAML config file, to standardize your workflow, and use a collection of test cases
- with the [ComfyLab `Output Config` node](../../Output%20Config/)
- ...
If you want to dive deeper, I propose we move to the **next (and last) tutorial of the XY Plot series: 03 - advanced techniques**.\
But before doing that, please note:
- it addresses some edge cases that may not be needed for most
- it is not as detailed as those 2 first tutorials, we'll go faster
- if you haven't done it yet, I strongly advise you read the [XY Plot core concepts](../../../node%20reference/xy%20plot/00%20-%20core%20concepts.md): it's not long, and you may learn some important design choices there.
Interested? Let's go to the next tutorial: **[03 - advanced techniques](../03%20-%20advanced%20techniques/)**
Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

@@ -0,0 +1,808 @@
{
"last_node_id": 19,
"last_link_id": 25,
"nodes": [
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
930,
550
],
"size": [
210,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
},
{
"name": "text",
"type": "STRING",
"link": 25,
"widget": {
"name": "text"
}
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 10,
"type": "XYPlotQueue",
"pos": [
30,
100
],
"size": [
315,
146
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "dim1",
"type": "LIST",
"link": 16,
"shape": 6
},
{
"name": "dim2",
"type": "LIST",
"link": 21,
"shape": 6,
"label": "dim2 (opt)"
}
],
"outputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"links": [
11
],
"slot_index": 0,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "dim1_value",
"type": "*",
"links": [
23
],
"slot_index": 1,
"label": "dim1 value"
},
{
"name": "dim2_value",
"type": "*",
"links": [
24
],
"slot_index": 2,
"label": "dim2 value"
}
],
"properties": {
"Node name for S&R": "XYPlotQueue"
},
"widgets_values": [
0,
0,
0
]
},
{
"id": 11,
"type": "XYPlotRender",
"pos": [
1250,
100
],
"size": [
315,
186
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"link": 11,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "image",
"type": "IMAGE",
"link": 10
},
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"link": 22,
"shape": 7,
"label": "config: grid (opt)"
},
{
"name": "plot_config_header",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: header (opt)"
},
{
"name": "plot_config_footer",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: footer (opt)"
}
],
"outputs": [
{
"name": "grid",
"type": "IMAGE",
"links": [
14
],
"slot_index": 0
},
{
"name": "image",
"type": "IMAGE",
"links": [
15
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "XYPlotRender"
},
"widgets_values": [
"{dim1}",
"{dim2}",
true
]
},
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
474
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
1056457549688881,
"randomize",
40,
7,
"euler",
"normal",
1
]
},
{
"id": 13,
"type": "PreviewImage",
"pos": [
1600,
100
],
"size": [
510,
450
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 14
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 14,
"type": "ListFromMultiline",
"pos": [
-370,
100
],
"size": [
300,
210
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
16
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"a man wearing a blue coat\na woman wearing a red dress"
]
},
{
"id": 12,
"type": "PreviewImage",
"pos": [
1300,
360
],
"size": [
230,
170
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 15
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 19,
"type": "FormatMultiline",
"pos": [
40,
290
],
"size": [
290,
140
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "arg0",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": 23,
"label": "arg0"
},
{
"name": "arg1",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": 24,
"label": "arg1"
},
{
"name": "arg2",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": null
}
],
"outputs": [
{
"name": "formatted",
"type": "STRING",
"links": [
25
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FormatMultiline"
},
"widgets_values": [
"a full-body shot photo of {arg0}, {arg1}, hyperrealistic, hyperrealism, 8k, cinematic lighting"
]
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
40,
480
],
"size": [
315,
98
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
},
{
"id": 17,
"type": "ListFromMultiline",
"pos": [
-360,
360
],
"size": [
290,
200
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
21
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"walking in a street in NYC\neating in a Japanese restaurant"
]
},
{
"id": 18,
"type": "PlotConfigGrid",
"pos": [
770,
-180
],
"size": [
315,
250
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"links": [
22
],
"slot_index": 0,
"label": "config: grid"
}
],
"properties": {
"Node name for S&R": "PlotConfigGrid"
},
"widgets_values": [
40,
"#393939",
"Roboto-Italic.ttf",
40,
"#bbb",
50,
70,
20,
10
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
10,
8,
0,
11,
1,
"IMAGE"
],
[
11,
10,
0,
11,
0,
"XY_PLOT_DATA"
],
[
14,
11,
0,
13,
0,
"IMAGE"
],
[
15,
11,
1,
12,
0,
"IMAGE"
],
[
16,
14,
0,
10,
0,
"LIST"
],
[
21,
17,
0,
10,
1,
"LIST"
],
[
22,
18,
0,
11,
2,
"PLOT_CONFIG_GRID"
],
[
23,
10,
1,
19,
0,
"STRING,INT,FLOAT,BOOLEAN"
],
[
24,
10,
2,
19,
1,
"STRING,INT,FLOAT,BOOLEAN"
],
[
25,
19,
0,
6,
1,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8264462809917354,
"offset": [
222.26954547197894,
239.23502174146589
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "e957a7e4d74579f81e3f4457b8d672327c8ac7f7"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

@@ -0,0 +1,904 @@
{
"last_node_id": 21,
"last_link_id": 27,
"nodes": [
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
930,
550
],
"size": [
210,
46
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
},
{
"name": "text",
"type": "STRING",
"link": 25,
"widget": {
"name": "text"
}
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 10,
"type": "XYPlotQueue",
"pos": [
30,
100
],
"size": [
315,
146
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "dim1",
"type": "LIST",
"link": 16,
"shape": 6
},
{
"name": "dim2",
"type": "LIST",
"link": 21,
"shape": 6,
"label": "dim2 (opt)"
}
],
"outputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"links": [
11
],
"slot_index": 0,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "dim1_value",
"type": "*",
"links": [
23
],
"slot_index": 1,
"label": "dim1 value"
},
{
"name": "dim2_value",
"type": "*",
"links": [
24
],
"slot_index": 2,
"label": "dim2 value"
}
],
"properties": {
"Node name for S&R": "XYPlotQueue"
},
"widgets_values": [
0,
0,
0
]
},
{
"id": 11,
"type": "XYPlotRender",
"pos": [
1250,
100
],
"size": [
315,
186
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"link": 11,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "image",
"type": "IMAGE",
"link": 10
},
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"link": 22,
"shape": 7,
"label": "config: grid (opt)"
},
{
"name": "plot_config_header",
"type": "PLOT_CONFIG_HF",
"link": 26,
"shape": 7,
"label": "config: header (opt)"
},
{
"name": "plot_config_footer",
"type": "PLOT_CONFIG_HF",
"link": 27,
"shape": 7,
"label": "config: footer (opt)"
}
],
"outputs": [
{
"name": "grid",
"type": "IMAGE",
"links": [
14
],
"slot_index": 0
},
{
"name": "image",
"type": "IMAGE",
"links": [
15
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "XYPlotRender"
},
"widgets_values": [
"{dim1}",
"{dim2}",
true
]
},
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
474
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
864603441980223,
"randomize",
40,
7,
"euler",
"normal",
1
]
},
{
"id": 13,
"type": "PreviewImage",
"pos": [
1600,
100
],
"size": [
510,
450
],
"flags": {},
"order": 14,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 14
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 14,
"type": "ListFromMultiline",
"pos": [
-370,
100
],
"size": [
300,
210
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
16
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"a man wearing a blue coat\na woman wearing a red dress"
]
},
{
"id": 12,
"type": "PreviewImage",
"pos": [
1300,
360
],
"size": [
230,
246
],
"flags": {},
"order": 15,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 15
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 19,
"type": "FormatMultiline",
"pos": [
40,
290
],
"size": [
290,
140
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "arg0",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": 23,
"label": "arg0"
},
{
"name": "arg1",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": 24,
"label": "arg1"
},
{
"name": "arg2",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": null
}
],
"outputs": [
{
"name": "formatted",
"type": "STRING",
"links": [
25
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FormatMultiline"
},
"widgets_values": [
"a full-body shot photo of {arg0}, {arg1}, hyperrealistic, hyperrealism, 8k, cinematic lighting"
]
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
40,
480
],
"size": [
315,
98
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
},
{
"id": 17,
"type": "ListFromMultiline",
"pos": [
-360,
360
],
"size": [
290,
200
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
21
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"walking in a street in NYC\neating in a Japanese restaurant"
]
},
{
"id": 18,
"type": "PlotConfigGrid",
"pos": [
440,
-170
],
"size": [
315,
250
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"links": [
22
],
"slot_index": 0,
"label": "config: grid"
}
],
"properties": {
"Node name for S&R": "PlotConfigGrid"
},
"widgets_values": [
40,
"#393939",
"Roboto-Italic.ttf",
40,
"#bbb",
50,
70,
20,
10
]
},
{
"id": 20,
"type": "PlotConfigHF",
"pos": [
850,
-390
],
"size": [
300,
230
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "plot_config_hf",
"type": "PLOT_CONFIG_HF",
"links": [
26
],
"slot_index": 0,
"label": "config: header / footer"
}
],
"properties": {
"Node name for S&R": "PlotConfigHF"
},
"widgets_values": [
"",
"ComfyLab XY Plot tutorials",
"",
"#4d5f74",
"Roboto-Bold.ttf",
60,
"#2993cd",
30
]
},
{
"id": 21,
"type": "PlotConfigHF",
"pos": [
850,
-110
],
"size": [
300,
230
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "plot_config_hf",
"type": "PLOT_CONFIG_HF",
"links": [
27
],
"slot_index": 0,
"label": "config: header / footer"
}
],
"properties": {
"Node name for S&R": "PlotConfigHF"
},
"widgets_values": [
"Made with ComfyLab...",
"",
"...and Love",
"transparent",
"Roboto-Regular.ttf",
45,
"white",
30
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
10,
8,
0,
11,
1,
"IMAGE"
],
[
11,
10,
0,
11,
0,
"XY_PLOT_DATA"
],
[
14,
11,
0,
13,
0,
"IMAGE"
],
[
15,
11,
1,
12,
0,
"IMAGE"
],
[
16,
14,
0,
10,
0,
"LIST"
],
[
21,
17,
0,
10,
1,
"LIST"
],
[
22,
18,
0,
11,
2,
"PLOT_CONFIG_GRID"
],
[
23,
10,
1,
19,
0,
"STRING,INT,FLOAT,BOOLEAN"
],
[
24,
10,
2,
19,
1,
"STRING,INT,FLOAT,BOOLEAN"
],
[
25,
19,
0,
6,
1,
"STRING"
],
[
26,
20,
0,
11,
3,
"PLOT_CONFIG_HF"
],
[
27,
21,
0,
11,
4,
"PLOT_CONFIG_HF"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.7513148009015777,
"offset": [
358.2503570852133,
476.5740951619572
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "e957a7e4d74579f81e3f4457b8d672327c8ac7f7"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

@@ -0,0 +1,95 @@
# Tutorials / XY Plot: 03 - advanced techniques
Welcome to this third (and last) tutorial of the XY Plot series!
Here we are going to see a few advanced techniques you may find useful.\
Of course, I cannot anticipate all the use cases, but by following this tutorial, I hope you will see the versatility of the ComfyLab XY Plot nodes, and understand how to adapt for your scenario.\
Here, no frills, we'll go straight to the point, to focus of the functionalities rather than fancy grid customization.
If you haven't yet, I highly recommend you follow (at least read) the first 2 tutorials ([1](../01%20-%20the%20basics/) & [2](../02%20-%20pimp%20my%20grid/)), and read the [XY Plot core concepts](../../../node%20reference/xy%20plot/00%20-%20core%20concepts.md).
Tutorial sections:
- [Part 1 - Various checkpoints (or LoRAs, ...)](#part-1---various-checkpoints-or-loras-)
- [Part 2 - Various resolutions and aspect ratios](#part-2---various-resolutions-and-aspect-ratios)
- [TL;DR / Conclusion](#tldr--conclusion)
> As for all nodes in this extension, you can get useful contextual information, by just **putting the mouse pointer over a node or its inputs / widgets / outputs**. This should help you understand some details, without reading the more detailed wiki pages (yet).
And as always, if you do not want to follow all the steps, you can jump directly to [the conclusion](#tldr--conclusion).
---
## Part 1 - Various checkpoints (or LoRAs, ...)
**Load either `workflow - part 1.json` or `workflow - part 1.png` into ComfyUI.**\
**Important:**
- in the first input list, adjust to checkpoints that are available in your ComfyUI instance
- do no add the `.safetensors` extension, it will be added automatically
Execute the workflow.
![result - part 1](./details/result%20-%20part%201.jpg)
Here we build a grid with 2 different checkpoints, vs a list of predefined seeds.
What is to be noticed:
- checkpoints:
- we have chosen not to include the `.safetensors` extension in the input
- it would have been displayed in the row headers, making them too long
- instead, we have used a `Format: String` node, to append `.safetensors` to the `dim1 value` output of the `Queue` node
- but by simply doing so, we cannot pipe the text output to the `Load Checkpoint` node, why?
- the `chkpt_name` widget does not accept any string, but a string that exists in the list
- so to bypass this limitation we simply convert the text output to Any, thanks to the `Convert to Any` node
- ![Any output](./details/detail%20-%20part%201%20-%20any.jpg)
- you can read more about the Any output in the [XY Plot core concepts](../../../node%20reference/xy%20plot/00%20-%20core%20concepts.md), and the [2nd tutorial of the `Output Config` node](../../Output%20Config/02%20-%20more%20options)
- seeds:
- theoritically, we should have converted the list values to integers, thanks to the `convert` combo widget
- but it also works (for seeds only AFAIK)
Lessons learned:
- combo widgets (as `chkpt_name` in `Load Checkpoint`) do not accept simple strings
- a workaround is to use an output with type Any (`"*"`)
- should we have included the `.safetensors` in the first input, we could have dropped the `Format: String` and `Convert to Any` node, because the type of `Queue` outputs is Any
- but this wouldn't be great in terms of display in the grid
- the checkpoint list is connected to `dim1`, not `dim2`, for a performance reason
- if you want: just switch dim1 / dim2 and check the processing time _(more in the core concepts page)_
> You can basically use this approach for any combo widget, in particular when loading models (LoRA, ...).\
> But be **very cautious** about the values you set in input: by using the Any type, we bypass the standard ComfyUI type checks, so the workflow may fail if the values are incorrect.
## Part 2 - Various resolutions and aspect ratios
**Load either `workflow - part 2.json` or `workflow - part 2.png` into ComfyUI.**\
Adjust the checkpoint to one available in your ComfyUI instance.\
Execute the workflow.
![result - part 2](./details/result%20-%20part%202.jpg)
What we did:
- include the ComfyLab `Resolution to Dimensions` node, to split the first input (`1024 x 1024`, ...) into width and height, piped into `Empty Latent Image`
- we could have done this differently, I chose this method as it is straightforward in this case
- we also used a 2nd input list to set rotations, piped into the `rotation` input of `Rotate Latent`
- remember, we can do that because the `Queue` outputs are of type Any
While probably not the most useful as such, this example demonstrates **the ability of the ComfyLab XY Plot to handle various resolutions or aspect ratios within the same grid**.
## TL;DR / Conclusion
In this tutorial, we have:
- seen how to handle various checkpoints (or virtually any sort of model)
- and optimized the grid display at the same time
- demonstrated the ability of the XY Plot to include images or various dimensions / apect ratios within the same grid
This tutorial is still in construction, so I will certainly add most techniques in the future, be sure to check.
Also, you will find [various examples](../../../examples/) in the wiki (with less explanations).
In the meantime, if you have any suggestion, feel free to open [a discussion](https://github.com/bugltd/ComfyLab-Pack/discussions).
Thank you for reading, enjoy!
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

@@ -0,0 +1,807 @@
{
"last_node_id": 20,
"last_link_id": 22,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
26,
474
],
"size": [
315,
98
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "ckpt_name",
"type": "COMBO",
"link": 21,
"widget": {
"name": "ckpt_name"
}
}
],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"v1-5-pruned-emaonly.ckpt"
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
1209,
188
],
"size": [
210,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
11
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 11,
"type": "PreviewImage",
"pos": [
1480,
420
],
"size": [
260,
246
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 13
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
474
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
},
{
"name": "seed",
"type": "INT",
"link": 22,
"widget": {
"name": "seed"
}
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
440313153020244,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
473,
609
],
"size": [
315,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
1024,
1024,
1
]
},
{
"id": 15,
"type": "ListFromMultiline",
"pos": [
-240,
180
],
"size": [
290,
200
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
18
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"# here we list seed values\n156680208700286\n715168739510668\n568208062071311"
]
},
{
"id": 14,
"type": "ListFromMultiline",
"pos": [
-430,
-100
],
"size": [
470,
200
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
15
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"# here we list the checkpoints\n# do not add the '.safetensors' extension, it will be appended later\nsd_xl_base_1.0\nJuggernaut-XI-byRunDiffusion"
]
},
{
"id": 13,
"type": "XYPlotQueue",
"pos": [
110,
-120
],
"size": [
315,
146
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "dim1",
"type": "LIST",
"link": 15,
"shape": 6
},
{
"name": "dim2",
"type": "LIST",
"link": 18,
"shape": 6,
"label": "dim2 (opt)"
}
],
"outputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"links": [
14
],
"slot_index": 0,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "dim1_value",
"type": "*",
"links": [
19
],
"slot_index": 1,
"label": "dim1 value"
},
{
"name": "dim2_value",
"type": "*",
"links": [
22
],
"slot_index": 2,
"label": "dim2 value"
}
],
"properties": {
"Node name for S&R": "XYPlotQueue"
},
"widgets_values": [
0,
0,
0
]
},
{
"id": 10,
"type": "PreviewImage",
"pos": [
1820,
180
],
"size": [
440,
320
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 12
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 12,
"type": "XYPlotRender",
"pos": [
1450,
170
],
"size": [
315,
186
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"link": 14,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "image",
"type": "IMAGE",
"link": 11
},
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"link": null,
"shape": 7,
"label": "config: grid (opt)"
},
{
"name": "plot_config_header",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: header (opt)"
},
{
"name": "plot_config_footer",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: footer (opt)"
}
],
"outputs": [
{
"name": "grid",
"type": "IMAGE",
"links": [
12
],
"slot_index": 0
},
{
"name": "image",
"type": "IMAGE",
"links": [
13
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "XYPlotRender"
},
"widgets_values": [
"{dim1}",
"{dim2}",
true
]
},
{
"id": 20,
"type": "FormatString",
"pos": [
470,
-100
],
"size": [
210,
78
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "arg0",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": 19,
"label": "arg0"
},
{
"name": "arg1",
"type": "STRING,INT,FLOAT,BOOLEAN",
"link": null
}
],
"outputs": [
{
"name": "formatted",
"type": "STRING",
"links": [
20
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FormatString"
},
"widgets_values": [
"{arg0}.safetensors"
]
},
{
"id": 19,
"type": "ConvertToAny",
"pos": [
730,
-100
],
"size": [
168,
26
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "value",
"type": "*",
"link": 20
}
],
"outputs": [
{
"name": "value_any",
"type": "*",
"links": [
21
],
"slot_index": 0,
"label": "value (Any)"
}
],
"properties": {
"Node name for S&R": "ConvertToAny"
},
"widgets_values": []
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
11,
8,
0,
12,
1,
"IMAGE"
],
[
12,
12,
0,
10,
0,
"IMAGE"
],
[
13,
12,
1,
11,
0,
"IMAGE"
],
[
14,
13,
0,
12,
0,
"XY_PLOT_DATA"
],
[
15,
14,
0,
13,
0,
"LIST"
],
[
18,
15,
0,
13,
1,
"LIST"
],
[
19,
13,
1,
20,
0,
"STRING,INT,FLOAT,BOOLEAN"
],
[
20,
20,
0,
19,
0,
"*"
],
[
21,
19,
0,
4,
0,
"COMBO"
],
[
22,
13,
2,
3,
4,
"INT"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
508.67246365224605,
354.231567790153
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "61510174fd470093c9f63ec385cdc72f8a2f0894"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

@@ -0,0 +1,830 @@
{
"last_node_id": 22,
"last_link_id": 31,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
413,
389
],
"size": [
425.27801513671875,
180.6060791015625
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [
1209,
188
],
"size": [
210,
46
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
11
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 11,
"type": "PreviewImage",
"pos": [
1480,
420
],
"size": [
260,
246
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 13
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
422.84503173828125,
164.31304931640625
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
26,
474
],
"size": [
315,
98
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"sd_xl_base_1.0.safetensors"
]
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
474
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 26
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
134178653729123,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 14,
"type": "ListFromMultiline",
"pos": [
-410,
-110
],
"size": [
220,
210
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
15
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"# image dimensions\n1024 x 1024\n896 x 1152\n1216 x 832"
]
},
{
"id": 15,
"type": "ListFromMultiline",
"pos": [
-410,
160
],
"size": [
218.39999389648438,
220
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "list",
"type": "LIST",
"links": [
18
],
"slot_index": 0,
"shape": 6
},
{
"name": "count",
"type": "INT",
"links": null
}
],
"properties": {
"Node name for S&R": "ListFromMultiline"
},
"widgets_values": [
0,
true,
"disabled",
"# rotations\nnone\n90 degrees"
]
},
{
"id": 13,
"type": "XYPlotQueue",
"pos": [
-150,
-100
],
"size": [
315,
146
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "dim1",
"type": "LIST",
"link": 15,
"shape": 6
},
{
"name": "dim2",
"type": "LIST",
"link": 18,
"shape": 6,
"label": "dim2 (opt)"
}
],
"outputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"links": [
14
],
"slot_index": 0,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "dim1_value",
"type": "*",
"links": [
29
],
"slot_index": 1,
"label": "dim1 value"
},
{
"name": "dim2_value",
"type": "*",
"links": [
28
],
"slot_index": 2,
"label": "dim2 value"
}
],
"properties": {
"Node name for S&R": "XYPlotQueue"
},
"widgets_values": [
0,
0,
0
]
},
{
"id": 22,
"type": "ResolutionToDims",
"pos": [
190,
-110
],
"size": [
252,
102
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "resolution",
"type": "STRING",
"link": 29,
"widget": {
"name": "resolution"
}
}
],
"outputs": [
{
"name": "width",
"type": "INT",
"links": [
30
],
"slot_index": 0
},
{
"name": "height",
"type": "INT",
"links": [
31
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "ResolutionToDims"
},
"widgets_values": [
"1024x1024",
1
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
500,
-10
],
"size": [
315,
106
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "width",
"type": "INT",
"link": 30,
"widget": {
"name": "width"
}
},
{
"name": "height",
"type": "INT",
"link": 31,
"widget": {
"name": "height"
}
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
25
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
1024,
1024,
1
]
},
{
"id": 21,
"type": "LatentRotate",
"pos": [
860,
10
],
"size": [
210,
70
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 25
},
{
"name": "rotation",
"type": "COMBO",
"link": 28,
"widget": {
"name": "rotation"
}
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
26
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LatentRotate"
},
"widgets_values": [
"none"
]
},
{
"id": 10,
"type": "PreviewImage",
"pos": [
1820,
180
],
"size": [
720,
640
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 12
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 12,
"type": "XYPlotRender",
"pos": [
1450,
170
],
"size": [
315,
186
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "xy_plot_data",
"type": "XY_PLOT_DATA",
"link": 14,
"label": "XY plot data",
"color_off": "#52395B",
"color_on": "#9521c3"
},
{
"name": "image",
"type": "IMAGE",
"link": 11
},
{
"name": "plot_config_grid",
"type": "PLOT_CONFIG_GRID",
"link": null,
"shape": 7,
"label": "config: grid (opt)"
},
{
"name": "plot_config_header",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: header (opt)"
},
{
"name": "plot_config_footer",
"type": "PLOT_CONFIG_HF",
"link": null,
"shape": 7,
"label": "config: footer (opt)"
}
],
"outputs": [
{
"name": "grid",
"type": "IMAGE",
"links": [
12
],
"slot_index": 0
},
{
"name": "image",
"type": "IMAGE",
"links": [
13
],
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "XYPlotRender"
},
"widgets_values": [
"{dim1}",
"{dim2}",
false
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
11,
8,
0,
12,
1,
"IMAGE"
],
[
12,
12,
0,
10,
0,
"IMAGE"
],
[
13,
12,
1,
11,
0,
"IMAGE"
],
[
14,
13,
0,
12,
0,
"XY_PLOT_DATA"
],
[
15,
14,
0,
13,
0,
"LIST"
],
[
18,
15,
0,
13,
1,
"LIST"
],
[
25,
5,
0,
21,
0,
"LATENT"
],
[
26,
21,
0,
3,
3,
"LATENT"
],
[
28,
13,
2,
21,
1,
"COMBO"
],
[
29,
13,
1,
22,
0,
"STRING"
],
[
30,
22,
0,
5,
0,
"INT"
],
[
31,
22,
1,
5,
1,
"INT"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
520.672463652246,
237.231567790153
]
},
"node_versions": {
"comfy-core": "0.3.10",
"ComfyLab-Pack": "3b61f457a25c43f16f76dfa98fafd4a7bbc56c37"
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

+49
View File
@@ -0,0 +1,49 @@
# Tutorials / XY Plot
The `XY Plot` nodes allow you to create the grids you want, with virtually no limitation and many customization options:
- simple to use by default: only 2 nodes
- optional configuration nodes to customize the grid, or the page header / footer
- with possible pagination, row / column switch
- auto-queuing
- you can virtually make anything vary: CFG, seed, checkpoint / sampler / LoRA / ...
- not restricted to KSampler: can be adapted to any process generating images
- many configuration options:
- font (type / size / color)
- in row / column headers, and/or page header / footer
- background color:
- make your grid transparent (RGBA) to add a custom background image
- padding, wrap, ...
- mix any image resolution / aspect ratio
If needed, you can also check the [node references](../../node%20reference/xy%20plot/) for the [core concepts](../../node%20reference/xy%20plot/00%20-%20core%20concepts.md) and detailed node information.
## Tutorial 01 - the basics
**[>> Go to the tutorial](./01%20-%20the%20basics/)**
Contents:
- our first grid: list input, queue processing, ...
- string templating
- rows / columns and pagination
## Tutorial 02 - pimp my grid
**[>> Go to the tutorial](./02%20-%20pimp%20my%20grid/)**
Contents:
- customize the grid: background color, font, gap, ...
- add a page header and/or footer, and configure them separately
- make the grid RGBA to add a custom background inage
- display 'page X / Y'
## Tutorial 03 - advanced techniques
**[>> Go to the tutorial](./03%20-%20advanced%20techniques/)**
Contents:
- various checkpoints (or virtually any model, LoRA, ...)
- integrate various image resolutions / aspect ratios in the grid