ComfyUI Extension: Thought Bubble
ThoughtBubble is a custom node for ComfyUI that provides an interactive canvas to build and manage your prompts in a more visual and organized way. Think of it as a whiteboard for your ideas, allowing you to link different concepts, create conditional logic, and dynamically generate prompts using a powerful set of commands.
Custom Nodes (0)
README
ThoughtBubble for ComfyUI ðŸ§
ThoughtBubble is a custom node for ComfyUI that gives you a dynamic, visual, and node-based "whiteboard" to build and manage your prompts.
Stop trying to manage complex prompts in a single tiny text box! ThoughtBubble lets you break your ideas into logical pieces on an infinite canvas. You can build prompts by combining text boxes, create powerful wildcards with smart ListBoxes, and even set up complex conditional logic and loops using a simple command system.

The above example image uses a 2D array to loop through 9 possible prompts. Conditional commands are use to parse the selected prompt from the text array to further control the output.
Features
- Visual Prompting: Create, move, and resize "thought bubbles" on an infinite canvas. Pan, zoom, and organize your ideas visually.
- Dynamic Commands: Use a simple command syntax (
v(),w(),i(), etc.) to reference other boxes, run logic, and build your prompt. - Powerful Box Types:
- Text Box: Your standard workhorse for prompt fragments.
- List Box: A super-powered wildcard manager. Includes a toolbar to Sort, Shuffle, Append Files, and Parse/Apply Weights (
item:3). - Controls Box: Create custom variables (e.g., counters, random seeds) to control your
i()andw()commands for advanced loops and animations. - Area Box: Define regions for area conditioning (
a()) with a visual canvas editor.
- Full Wildcard Integration: Use
w(filename)ori(filename)to pull from yourComfyUI/user/wildcardstext files. - ListBox as Wildcard: Use a
ListBoxexactly like a text file!w(my_list_box_title)andi(my_list_box_title)work just as you'd expect. - Theming: Customize the look and feel of your canvas. Save, load, and share your themes.
- Autocomplete: The editor automatically suggests
lora(...),embed(...), ando(...)(text file) names as you type.
Installation
- Navigate to your
ComfyUI/custom_nodes/directory. - Clone this repository:
git clone https://github.com/matthewfriedrichs/ComfyUI-ThoughtBubble.git - Restart ComfyUI.
How to Use (The Basics)
- Add the ThoughtBubble node to your workflow (found in the "Workflow Efficiency" category).
- By default, the node will look for a box titled
output. The content of this box is what will be sent as your final prompt. - You can also Maximize (the
🗖button) any other box to use its content as theoutputinstead.
Canvas Controls
- Create a Box: Double-click on the canvas to open the creation menu.
- Create a Box (Quick): Left-click and drag on an empty area to create a new box of the last selected type.
- Move a Box: Click and drag a box's header.
- Resize a Box: Click and drag the bottom-right corner of any box.
- Pan Canvas: Middle-click (or scroll-wheel click) and drag.
- Zoom Canvas: Use the scroll wheel.
- Edit Title: Double-click a box's title to rename it.
The Box Types
1. Text Box (Default)
This is your standard box. Use it to write parts of your prompt, define reusable styles, or as the main output box.
- Example: Create a box titled
styleand putcinematic, 4k, hyperrealisticinside it. In youroutputbox, you can then writea cat, v(style).
2. List Box (Your Wildcard Manager)
This is the most powerful box type. Use it to create and manage lists of items, which can then be used just like wildcard files. It has a dedicated toolbar:
- Shuffle: Randomly shuffles all lines in the list.
- Unique: Removes all duplicate lines.
- Weights: Opens a modal to manage item weights. It parses
item:weightsyntax (likered:3,blue:1) to populate the inputs. You can then apply your changes back as duplicates (red, red, red, blue) or as syntax (red:3,blue:1). - Sort: Opens a modal to sort the list alphabetically (A-Z or Z-A).
- Append: Opens a file browser to merge another list from your
user/wildcardsoruser/textfilesfolders. You can choose to add it to the beginning or end, and to automatically deduplicate the merged list.
3. Controls Box (Advanced Loops)
This box doesn't hold text. Instead, it lets you create stateful variables that control your commands.
- Create a "Controls" box (e.g., title
my_controls). - Click "+ Add Variable" and give it a name (e.g.,
frame_counter). - Set its Behavior:
- Increment: Adds 1 on every run.
- Decrement: Subtracts 1 on every run.
- Randomize: Generates a new large random number every run.
- Fixed: Stays at the value you set.
- In any
i()orw()command, you can link it to this variable. Just typei(orw(and a dropdown menu will appear, letting you selectmy_controls / frame_counter. - Now, that specific
i()command will be controlled by yourframe_countervariable instead of the main node's iterator, allowing for multiple, independent loops in a single prompt.
4. Area Box (Regional Prompting)
This box is for Area Conditioning. It provides a visual canvas to define a region (X, Y, Width, Height, Strength) and a text area for the prompt you want to apply only to that region.
- To use it, create an Area Box, give it a title (e.g.,
face_area), and define your region and prompt. - In your
outputbox, use thea()command:a beautiful woman a(face_area).
Main Toolbar
At the top of the node is the main toolbar:
- Save/Load: Saves or loads text from the currently focused
TextBoxorListBox.ListBoxcontent saves to/loads fromuser/wildcards.TextBoxcontent saves to/loads fromuser/textfiles.
- Fit View: Zooms and pans the canvas to show all your boxes.
- Theme: Opens the theme editor to customize all colors and fonts.
- Grid: Changes the size of the background grid.
- Hide/Show Grid: Toggles the grid visibility.
- Periods = BREAK: Toggles whether periods (
.) are automatically converted to ComfyUI'sBREAKkeyword. - Hide/Show Map: Toggles the mini-map in the bottom-right corner.
- Run: 0 / Reset: Shows the current run number (iterator). "Reset" sets it back to 0.
Core Commands (The Fun Part)
Here are all the commands you can use inside any text or list box.
v(...) - Variables and Boxes
The most fundamental command. It gets content from other boxes or variables.
- Get Box Content:
v(box_title)- Example:
a beautiful v(subject)will pull the text from the box namedsubject.
- Example:
- Define a Variable:
v(var_name|value)- Example:
v(my_color|red) a v(my_color) carbecomesa red car. Thev(my_color|red)part becomes invisible.
- Example:
- Combine Boxes:
v(box1 + box2)- Example:
v(subject + style + location)
- Example:
- Subtract/Toggle Boxes:
v(box1 - box2)- This "toggles" the content of
box2. Ifbox2contains positive prompts, they become negative. If it contains negative prompts (-(ugly)), they become positive (ugly). - Example:
v(positive_prompt - negative_prompt)
- This "toggles" the content of
w(...) - Wildcard (Random)
Selects a random line from an inline list, a ListBox, or a user/wildcards file.
- Inline List:
w(red|green|blue)- Picks one:
red,green, orblue.
- Picks one:
- Weighted List:
w(red:3|blue:1)redis 3x as likely to be chosen asblue.
- ListBox / File:
w(my_list_box)orw(my_wildcard_file)- Picks one random line from the
ListBoxor file.
- Picks one random line from the
- Mixed:
w(my_list_box|a purple car|__other_wildcard__)- Picks randomly from all three options. If
my_list_boxis chosen, it then picks a random line from inside it.
- Picks randomly from all three options. If
i(...) - Iterator (Sequential)
Selects a line sequentially from a list, ListBox, or file. It uses the node's main "Run" counter or a linked Controls Box variable. It loops back to the start automatically.
- Inline List:
i(cat|dog|bird)- Run 0:
cat - Run 1:
dog - Run 2:
bird - Run 3:
cat
- Run 0:
- Weighted List (Holds):
i(cat:2|dog:1)- Run 0:
cat - Run 1:
cat(holds for 2 runs) - Run 2:
dog - Run 3:
cat
- Run 0:
- ListBox / File:
i(my_list_box)- Sequentially steps through every line in the
ListBoxor file.
- Sequentially steps through every line in the
- N-Dimensional (Combinations):
- Syntax:
i( (list_a) | (list_b) ) - Example:
i( (a|b) | (x|y) )- Run 0:
ax - Run 1:
ay - Run 2:
bx - Run 3:
by
- Run 0:
- Advanced Example:
a i( (red|green) | (car|truck) | in the (day|night) )- This will iterate through all 8 combinations (red car in the day, red car in the night, red truck in the day, ...).
- With ListBoxes:
i( (my_colors_list) | (my_objects_list) )- This is extremely powerful, iterating through every combination of your two lists.
- Syntax:
-(...) - Negative Prompt
Moves text from the positive prompt to the negative prompt.
- Example:
a beautiful painting -(ugly, deformed, bad hands) - Final Positive:
a beautiful painting - Final Negative:
ugly, deformed, bad hands
lora(...) - Load LoRA
Applies a LoRA. Autocomplete will help you find the name.
- Syntax:
lora(lora_name:model_strength)orlora(lora_name:model:clip) - Example:
lora(my_style_lora:0.8)
embed(...) - Load Embedding
Applies a Textual Inversion embedding. Autocomplete will help.
- Syntax:
embed(embedding_name) - Example:
embed(my_embedding)
o(...) - Open Text File
Loads text from a file in user/textfiles. This is useful for long, static prompt snippets.
- Example:
o(my_base_prompt)will insert the full text frommy_base_prompt.txt.
a(...) - Area Conditioning
Applies the prompt from an Area Box to your main prompt.
- Example:
a detailed face a(face_area) - Note: This command only works if you have an
Area Boxwith the titleface_area.
r(...) - Random Number
Generates a random number.
- Syntax:
r(max)orr(min|max)orr(min|max|decimals) - Example 1 (Int):
a stack of r(3|10) books->a stack of 7 books - Example 2 (Float):
(a cat:r(0.8|1.2|1))->(a cat:1.1)
?(...) - If Statement
A simple "if" statement. It checks the prompt before it for keywords. If the keywords are found, it outputs the "true" text, otherwise the "false" text.
- Syntax:
?(keyword:weight|text_if_true|text_if_false) - Note: The "true" text is only output if the sum of weights of found keywords is >= 1.0. A keyword with no weight defaults to 1.0.
- Example:
a photo of a cat. ?(cat|very cute|not a cat)- Output:
a photo of a cat. very cute
- Output:
- Weighted Example:
a man with a hat ?(man:0.5, hat:0.5|wearing a hat|not wearing a hat)- Output:
a man with a hat wearing a hat(because 0.5 + 0.5 = 1.0)
- Output:
??(...) - Multi-If Statement
A "switch" or "if/else if/else" statement. It checks for multiple conditions in order and outputs the text for the first one that is true.
- Syntax:
??(cond_1:text_1|cond_2:text_2|default_text) - Example:
a red car ??(red:is red|blue:is blue|is some other color)- Output:
a red car is red
- Output:
h(...) - Hidden Text
Hides text from the final prompt. This is useful for leaving comments or running v() set commands.
- Example:
a red car h(this is a test) h(v(my_var|blue)) - Output:
a red car(butmy_varis now set toblue).