πͺ π Text Aspect Ratio Finder
9β in the prompt and let the canvas follow it
- width
- height
- aspect_ratio
This one solves a specific, real annoyance: when you're iterating prompts you're also iterating canvas sizes, and the size lives in a widget two nodes away from the text you're actually editing. This node reads the ratio straight out of the text and hands you the pixel dimensions, so portrait, 9:16, film grain can become a 768Γ1368 latent without you touching a width field.
How it works
There are two matching modes, and which one you get depends on whether the allowed list is filled in.
With aspect_ratios populated (the default is eight common ratios), it builds a pattern per allowed ratio and matches only those, with guards on either side so 16:9 doesn't fire inside 116:9 or 16:90. If a number in your prompt looks like a ratio but isn't on your list, it's ignored. That strictness is the feature - you control exactly what's allowed to move your canvas.
With aspect_ratios empty, it goes open mode and accepts any N:N, NxN or N/N string it finds, decimals included, so 2.35:1 works.
search_mode then decides which of the matches wins: First match (Front) takes the earliest in the text, Last match (Back) the last. No match falls through to default_aspect_ratio.
The maths is then straightforward: target_mp is clamped between min_mp and max_mp, the aspect ratio turns that megapixel budget into raw width and height, and both are rounded to the nearest multiple of multiple_of (default 8, minimum 8, and that floor exists because diffusion models are built on a latent grid). Divisible-by-8 is the safe universal default; some architectures want 64.
If the fallback ratio is itself malformed, the node prints a warning, shows an error state on the node in the UI, and gives you a 1:1 rather than failing the run.
Inputs and outputs
The ones you actually touch: aspect_ratios (your whitelist, comma-separated), search_mode, target_mp (default 1.0), and default_aspect_ratio (default 1:1). The advanced ones are multiple_of, min_mp and max_mp. text is an optional input - wire it from your prompt source.
Outputs are width and height as INTs plus aspect_ratio as a STRING. Width and height go into whatever node you use for an empty latent, or into the resolution inputs of a text-to-image node. The ratio string is nice on a filename or in the readout node.
What number to expect
At the default 1.0 MP, a 16:9 request lands around 1368Γ768. That's megapixel maths, not bucket snapping - and it matters if you're on an older architecture with a fixed set of trained resolutions. SDXL's trained widescreen buckets are 1344Γ768 and 1536Γ640, and its native square is 1024Γ1024. Newer models are far more forgiving, mostly taking anything in a roughly 1β2 MP band and degrading softly rather than duplicating anatomy, which is exactly the regime where a floating target_mp makes sense.
So: if you're chasing one specific model's exact trained buckets, keep typing the resolution and use this node for the cases where the ratio is the variable.
Install
ComfyUI Manager β search SaturnNodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/KOFiblto/ComfyUI-SaturnNodes
Restart. Pure Python - nothing from the pack's Pillow/numpy/piexif requirements is needed for this one. Category: πͺ SaturnNodes/Utils. Older graphs calling it AspectRatioFinder still resolve.
Where people get burned
Your prompt has numbers that look like ratios. steps 20:30 or a seed range, or 1:2 inside a weight notation. The whitelist is your defence: keep aspect_ratios tight, or quote-light your prompt and drop the stray token. Open mode (empty list) will happily match anything ratio-shaped in the text.
The canvas came out portrait when you wanted landscape. search_mode picked the wrong end of the text and grabbed the other ratio. Or the ratio appeared twice. Switch to Last match (Back) and put the ratio you mean at the end.
target_mp didn't take. It's clamped to the min_mp/max_mp window, so a value below min_mp or above max_mp silently becomes the bound. Both default to a wide range, but if you've narrowed them for safety, that's your culprit.
Dimensions aren't what you calculated by hand. The multiple_of rounding does that; with a step of 4 you can nudge it, but the value has a floor of 8 for good reason - a latent that isn't divisible by the model's grid will either error or produce seams.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| aspect_ratios | STRING | 1:1, 2:3, 3:2, 3:4, 4:3, 9:16, 16:9, 21:9 | β |
| search_mode | COMBO | First match (Front) | 2 options: First match (Front), Last match (Back) |
| target_mp | FLOAT | 1.00.1β16 | β |
| default_aspect_ratio | STRING | 1:1 | β |
| multiple_of | INT | 88β128 | β |
| min_mp | FLOAT | 0.10.01β64 | β |
| max_mp | FLOAT | 16.00.1β64 | β |
| textopt | STRING | β |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| width | INT | β |
| height | INT | β |
| aspect_ratio | STRING | β |