Smart Latent
Latents at any size, without the OOM roulette
- vae
- image
- latent
- dims_json
- width
- height
- bbox_json
Most people find this node when their SDXL workflow OOMs at an unusual resolution, or when they type 1346×902 into Empty Latent and get back an image that looks subtly wrong. Smart Latent is the pack's answer to both: it creates empty latents or encodes images at any H×W, snaps everything safely to 64-multiples, and wraps the VAE so tiled encode/decode kicks in automatically when your VAE supports it. It's the workhorse of the SDXL Adherence pack (regiellis/ComfyUI-SDXL-Adherence) and the node the other helpers orbit.
SDXL doesn't like off-grid sizes - anything not on the 64 grid gets silently squished, and big canvases on a 24 GB card blow up VRAM. Smart Latent's whole job is to make "any size in, safe size out" a one-node operation.
How it works
mode picks between empty (allocate a blank latent - new images) and encode_image (VAE-encode a reference image for img2img or inpaint). In empty mode, the resolution dropdown has the standard SDXL presets (1024×1024, 1216×832, 1344×768, the lot), and selecting one overrides width/height. Then the snapping policies kick in:
pad_up(default) - letterbox to the next 64-multiple. Keeps all content, which is why it's the recommended default.downscale_only- fit inside the nearest 64, then pad the small residual.resize_round- resize near the nearest 64; changes aspect a bit.crop_center- centered crop down to the lower 64, no resize.
Padding is alpha-aware - the RGB channels pad with your chosen pad_kind (reflect by default, with an automatic fallback to replicate when reflect would be illegal), while alpha pads with replicate/constant so you don't get halos. And max_pixels (default 1024×1024) acts as the VRAM ceiling: if your requested size exceeds it, the node downscales by 64-steps until it fits. max_long_side is a second, optional guard that pre-shrinks the input image before snapping.
The tiled VAE part is where it earns its keep: it probes several encode_tiled/decode_tiled signatures and falls back to plain encode/decode if your VAE doesn't have them. Bigger images on the same card, fewer OOMs.
Inputs and outputs
You set four things in practice: vae, mode, width, height - plus resolution if you'd rather click a preset. The rest (tile_size, tile_overlap, max_pixels, max_long_side, pad_*, batch, seed) are guardrails and tuning you only touch when something breaks.
Outputs: latent goes to the KSampler. width/height are the actual snapped dims, dims_json is metadata, and bbox_json is the critical one - it records where your real content sits inside the padded canvas, so Crop By BBox can cut the padding back off after decode. Keep bbox_json wired straight through.
Installing it
Same single install as the rest of the pack:
cd ComfyUI/custom_nodes
git clone https://github.com/regiellis/ComfyUI-SDXL-Adherence
Restart ComfyUI, or search "SDXL Adherence" in ComfyUI Manager. No model downloads, no dependencies beyond what ComfyUI already ships.
Troubleshooting
- OOM at high res. Drop
tile_sizefrom 320 toward 256, lowermax_pixels, or setmax_long_sideto cap the input. The pack's own speed notes suggest exactly this. - Latent mismatch errors. The node asserts the encoded latent is exactly
W/8 × H/8. If you get a mismatch, you've fed it a VAE whose tiled path returns something unexpected - switchuse_tiledoff and see if it clears. - Padding you didn't ask for.
pad_upis the default for a reason, but if you want no letterboxing at all,resize_roundorcrop_centeravoid it. And whatever you choose, remember the decode comes back padded too - finish with Crop By BBox.
This is the one node in the pack you could reasonably call essential. Everything else is polish; Smart Latent is the plumbing.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| vae | VAE | VAE model used to create or encode latents. | |
| mode | COMBO | Create an empty latent or encode an input image. | |
| width | INT | 102464–4096 | Requested width; may downscale by 64s to respect max_pixels. |
| height | INT | 102464–4096 | Requested height; may downscale by 64s to respect max_pixels. |
| resolutionopt | COMBO | Use width/height | Quick presets; selecting one overrides width/height (empty mode). |
| snap_modeopt | COMBO | pad_up | Snap policy for non-64 dims: pad, downscale, resize, or crop. |
| pad_kindopt | COMBO | reflect | Padding type for pad_up/downscale (reflect avoids seams). 'edge' behaves like 'replicate'. |
| pad_valueopt | INT | 1280–255 | Pad value (constant mode only). |
| batchopt | INT | 11–16 | Batch size for empty latent or image batch. |
| imageopt | IMAGE | Image tensor when mode = encode_image. | |
| tile_sizeopt | INT | 320192–512 | Tile size for VAE.encode_tiled if available. |
| tile_overlapopt | INT | 320–256 | Tile overlap for tiled VAE encode/decode. |
| force_bchwopt | BOOLEAN | true | Ensure image is [B,C,H,W] before encoding. |
| use_tiledopt | BOOLEAN | true | Use tiled VAE encode/decode when available. |
| seedopt | INT | 0 | Reserved for future use. |
| max_pixelsopt | INT | 104857665536–16777216 | Upper bound on W*H; uniformly downscale to fit if exceeded. |
| max_long_sideopt | INT | 00–8192 | Optional VRAM guard: if >0, downscale input image so max(H,W) <= this before snapping. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |
| dims_json | STRING | — |
| width | INT | — |
| height | INT | — |
| bbox_json | STRING | — |