Load Mask Tensor
Get yesterday's mask back, in full float precision
- masks
This is the other half of the deal, and it's the half that actually saves you time. You ran SAM 3 over a clip, saved the mask with Save Mask Tensor, and now you want to change the inpaint prompt without sitting through segmentation again. Load Mask Tensor reads that .pt file straight off disk and hands you back a MASK you can wire into anything.
It's the mask_io pair from swanjohn99/comfyUIcostumNodes - a small, new, single-author pack whose whole pitch is exact round-tripping.
Why a saved mask is worth having
Masks are the expensive part of a promptable-masking pipeline. In the current 2026 workflows - the LTX-2 inpaint setups, anything where you describe a region in words and let SAM 3 find it - the segmentation pass is the slow, memory-hungry step. Denoise, mask blur, grow, the prompt inside the region: none of that needs the segmenter re-run. Splitting the graph at the mask file is the cheap way to iterate.
There's a fidelity argument too. A mask saved as a grayscale PNG is 8-bit, which is visibly stepped on a soft edge. A .pt keeps the original float32 values, pixel for pixel. If you're going to keep a mask around across sessions, keep the real thing.
How it works
The loader lists *.pt files in output/<subfolder>/ and turns that listing into the mask_file dropdown. On queue, it reads the file with torch.load(..., weights_only=True), unwraps it if it happens to be a dict containing a masks or mask key, checks that it really is a tensor, and normalizes the shape to ComfyUI's [B, H, W] convention - a flat [H, W] gets a batch dimension, [B, 1, H, W] or [B, H, W, 1] gets squeezed, anything stranger raises Unsupported MASK shape. Values are cast to float32 and handed over as-is.
It also implements IS_CHANGED on the file's modification time, so re-saving over a mask re-triggers the graph instead of serving a cached result. Small thing; it's the difference between "works" and "why is it using the old mask".
The inputs that matter
mask_file- the dropdown. It's built by scanning the subfolder at the moment your graph loads the node definition, and it shows(none)when the folder is empty. There's also a "select" button that opens a picker with sizes and timestamps, if you'd rather not squint at the dropdown.subfolder- defaultmasks. This must match what you typed in Save Mask Tensor, or the node will look in a directory that doesn't exist and error with the full path so you can see the mismatch.
There's one output: masks (MASK, a float tensor [B, H, W]). Wire it wherever a mask goes - the inpaint conditioning nodes, a mask grow/blur pass, or a masked composite. Because it's the plain MASK type, nothing downstream needs to know it came from a file.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/swanjohn99/comfyUIcostumNodes
Restart ComfyUI - that's the entire install. No pip packages, no checkpoints, no Triton (a relief if you've fought Triton to get a SAM 3 node running on Windows). It's published under the display name Mask Tensor I/O for the Comfy Registry, so try ComfyUI Manager for that name first; otherwise clone it.
Both nodes come in the same clone, so you install once and get the saver too - plus a pair of Save/Load nodes for MiniMax H3 AV latents.
Where people get burned
The dropdown is a snapshot. Save a new mask while the node is already sitting in your graph and it will not appear in the list, because the choices were computed when the node was created. Refresh the node definitions (or reload the workflow) and it shows up. This trips up roughly everyone the first time.
(none) is not a valid choice. Queue with it selected and you get a FileNotFoundError telling you to run Save Mask Tensor first - the pack assumes you're the one who made the files.
The files aren't in input/. This loader reads from output/<subfolder>, not the input directory. Drop a .pt into ComfyUI/input/masks and the node will never see it. Same story if you point ComfyUI at a different output directory between sessions: from the node's point of view, the mask is gone.
A deleted file stays in an old workflow. The name persists in the saved graph, so reopening a workflow after you cleaned out output/masks gives you a missing-file error rather than a fallback. Worth knowing before you go wild with the delete button.
Batch dimension is preserved. If the saved mask was a ten-frame video mask, you get ten masks out. Feeding that into a single-image pipeline is on you - the node returns exactly what was on disk, which is the point, but the mask's batch size becomes part of your graph's contract.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| mask_file | COMBO | 1 options: (none) | |
| subfolder | STRING | masks | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| masks | MASK | — |