Image Resize Area
Let sqrt(W×H) pick your resolution instead of you
- image
- mask
- upscale_model
- image
- mask
- int
You have a 4000×3000 photo, a 1216×832 render, and a model that's happy anywhere in a 1–2 megapixel band. Somewhere in your workflow you need those two to meet at a size that's plausible, aspect-correct, and a multiple of 32 - and you'd rather not type 2048 into five nodes.
That's the job of Image Resize Area from ComfyUI 1hewNodes. It measures the input, derives one number - N = sqrt(width × height), the side of the square with the same pixel count - clamps that number between two bounds you set, snaps it to a multiple, and resizes to the aspect-preserving size whose area is about N². Boring plumbing, excellent plumbing. If you've ever hand-tuned ImageScaleToTotalPixels per image, this is the same idea with a floor, a ceiling, and alignment.
Why the bounds matter
Nothing is trained on arbitrary resolutions. Flux wants multiples of 64; LTX video wants 32; most of the 2026 crop takes any size in a 1–2MP band but degrades softly above it. The old failure mode - 1920×1080 on SDXL, stretched anatomy - is why "generate at native, upscale after" is a reflex.
This node gives you that band as two integers. min_sq_area is the floor, max_sq_area the ceiling, both on the square side (defaults 1024 and 2048, i.e. roughly 1MP to 4MP of budget), and divisible_by is the alignment (default 32). Set them once, and everything that flows through lands in range and on-grid.
The mechanism, and the three branches
N is computed as sqrt(W × H), rounded to the nearest multiple of divisible_by, then clamped into the multiples that actually exist inside [min_sq_area, max_sq_area]. From there:
- Inside the range - the image and mask pass through untouched. Note that
Nis still reported, and it can differ from your actual image size. That's deliberate:intis the aligned reference, not a measurement. - Above
max_sq_area- downscale withmethod. - Below
min_sq_area- upscale withmethod, or, if you wiredupscale_model, run the upscaler first and then normalise to target withmethod.
The target size keeps your aspect ratio: width is sqrt(ratio) × N, height is derived from it, and both sides are rounded up to a multiple of divisible_by. The author's own example: 4000×3000 in, max_sq_area = 2048 out → 2368×1792. Not a square, by design.
Inputs you actually touch
min_sq_area and max_sq_area are the whole personality of the node - set them to your model's megapixel band. divisible_by is the alignment: 32 is a safe default, 64 for Flux. method picks the sampler (lanczos default, plus nearest, bilinear, bicubic, hamming, box).
Two things worth knowing about the rest. fit (crop / pad / stretch) barely matters here, because the target already preserves aspect - it only absorbs the mismatch introduced by divisible_by rounding, so crop is the sane default. And pad_color is only read when fit = pad; it takes a grayscale value ("1.0" is white), hex, RGB triples, or the strategies edge, average, extend, mirror.
mask and upscale_model are optional. Feed a mask and it's transformed with the same fit and sampler as the image, which makes this safe in masked-edit chains. upscale_model is lazy: it's only requested from the upstream loader when the image is genuinely undersized, so an upscaler you wired defensively costs nothing on images that don't need it.
Outputs are image, mask, and int - the aligned N, handy as one authoritative number for a filename or a downstream size node.
Install
Through ComfyUI Manager, search ComfyUI-1hewNodes. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/1hew/ComfyUI-1hewNodes
# restart ComfyUI
Heads up: this node needs only Pillow, numpy and torch, but the pack declares a lot more in requirements.txt - opencv, scikit-image, scikit-learn, ultralytics, rembg, onnxruntime, av, torchaudio - because the pack also does background removal and video. When that install blows up (the usual onnxruntime / torchaudio-vs-torch fights), do it by hand:
cd ComfyUI/custom_nodes/ComfyUI-1hewNodes
pip install -r requirements.txt
Where people get burned
- No multiple of
divisible_byinside your bounds.min_sq_area = 100,max_sq_area = 110,divisible_by = 64raisesno multiple of 64 exists within [100, 110]. Give the range at least one step of headroom. - A mask whose size doesn't match the image is a hard error, not a silent stretch.
- Only the first frame decides. A batch is resized together off the first image's dimensions, so mixed-size batches will all end up at one size.
- Don't trust
intwhen the node passes through. In-range images are returned unchanged, so that output is a target, not a report. If you need real dimensions, use a size-reading node. - Swapped bounds don't crash.
min_sq_area > max_sq_areais silently normalised by swapping them, which is friendly and also exactly the kind of typo that makes you doubt your own settings.
One last bit of realism: this node landed in v3.29.0 and the pack has essentially no chatter in the wider community, so there's no pile of threads to fall back on. The author's bilingual docs in web/docs/1hew_ImageResizeArea/ are your real manual, and they're honest about the quirks above.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| min_sq_area | INT | 10241–8192 | — |
| max_sq_area | INT | 20481–8192 | — |
| method | COMBO | lanczos | 6 options: nearest, bilinear, lanczos, bicubic, hamming, box |
| fit | COMBO | crop | 3 options: crop, pad, stretch |
| pad_color | STRING | 1.0 | — |
| divisible_by | INT | 321–1024 | — |
| maskopt | MASK | — | |
| upscale_modelopt | UPSCALE_MODEL | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |
| int | INT | — |