🧰 Universal Latent IO (Save/Load/Pass)
Your sampler doesn't have to start from scratch
- latent
- latent_out
- filename
LatentIO is the sibling node in the UniversalIO pack, and it does for the latent what ConditioningIO does for the prompt: save it to disk, load it back, or just pass it through. A latent is the compressed representation your sampler actually works in - 4 channels on SD/SDXL-class VAEs, 16 on Flux, and nothing you'd ever look at as an image. So why persist something you can't see? Because it's the cheapest way in ComfyUI to make one generation feed the next without paying the VAE tax.
The classic pain it solves: multi-stage video. Wan 2.2 and friends split sampling into a high-noise pass and a low-noise pass, and on a consumer card the model swap between them is where OOMs and hard crashes happen. The community fix is to save the latent after stage one, unload the model, load the latent back, and run stage two fresh. On paper it's a two-minute detour; with ComfyUI's stock SaveLatent it's actually a chore, because that node writes to your output folder and you end up manually copying files between directories to keep stage two's file picker honest. LatentIO saves straight into ComfyUI/models/latents/, gives you a dropdown that lists them, and hands you the filename as a string output - the whole copy-paste dance disappears.
It's the same trick for image editing: the community's fix for the "every edit gets more saturated" problem on Klein, Nano Banana, and friends is to stop round-tripping through VAE encode/decode and keep working in latent space. Every encode→decode cycle is lossy; hold onto the latent and successive edits barely degrade. That technique is so clunky with stock nodes that people write it up as a survival guide; LatentIO removes the filing-cabinet part.
How it works
Three modes, exactly like its sibling. pass (default) is a straight pass-through - the node is invisible, which makes it safe to leave in any graph. save writes the latent dict to ComfyUI/models/latents/ as a .latent file via torch.save, and still passes the data through on latent_out. load reads one back and hands you a latent ready for a sampler or a second stage.
The inputs that matter, of the few there are:
save_path- empty gives you auto-numbered files (Latent_00001_.latent); a fixed name gives you overwrite-style saving so a rerun never piles up files.auto_delete- deletes the previous file before saving. With a fixedsave_path, the disk only ever holds the latest. Set-and-forget for a repeatable workflow.device-cpu(default) is the portable serialization choice;autosaves on whatever device the data is on and loads preferring a GPU backend (XPU→CUDA, CPU fallback);gpuforces VRAM and warns if none exists.
Outputs are latent_out (LATENT) and filename (STRING). It's an OUTPUT_NODE, so a save-only stage passes ComfyUI's "no outputs" validation. Bonus: it handles ComfyUI 0.30's NestedTensor video latent structure, which a lot of older save nodes choke on.
The two gotchas
- The
latent_filedropdown won't accept a STRING link from another node - ComfyUI's COMBO type just can't do it. Select by hand. - Identical-input saves get skipped by ComfyUI's node cache. Change a parameter if it's not re-saving.
Worth knowing about the lineage too: this pack is a refactor of ComfyUI-SaveLoadUniversalConditioningLatent, and the fixes are real - the old code saved files one directory outside ComfyUI, called a VRAM-clear that did nothing on Intel Arc, and mutated shared objects in place, which corrupted ComfyUI's cached outputs for other nodes. The rewrite uses the official folder_paths API, soft_empty_cache(), and shallow-copies instead of mutating. Less exciting than new features, but that's the difference between "works on my machine" and "works on Arc too."
Install
cd ComfyUI/custom_nodes
git clone https://github.com/hqh330/ComfyUI-UniversalIO.git
Restart ComfyUI, then find both nodes under the UniversalIO category (or search "Latent"/"Universal"). ComfyUI Manager has it under "ComfyUI-UniversalIO" too. No extra models, no dependencies - it's pure torch.
The mental model that makes this node click: a .latent file is a paused generation. Save it, walk away, rent a bigger GPU, load it, resume - same numbers, same noise, no re-encoding. That's the whole trick.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| mode | COMBO | pass | 3 options: save, load, pass |
| latentopt | LATENT | — | |
| latent_fileopt | COMBO | 0 options: | |
| save_pathopt | STRING | save 模式自定义文件名(留空=自动编号)。填固定名 → 覆盖式保存不堆积。 | |
| auto_deleteopt | BOOLEAN | true | 临时文件模式:每次执行前自动删除旧文件(配合 save_path 固定名使用)。 |
| deviceopt | COMBO | cpu | 序列化/加载设备:auto=自动(保存跟随数据所在设备,加载优先 GPU 后端);cpu=通用(默认);gpu=强制 XPU/CUDA 显存(无 GPU 回退 CPU)。 |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| latent_out | LATENT | — |
| filename | STRING | — |