Write XMP Metadata
Stamp tags on fresh generations before they leave the graph
- image
- output_path
This is the "at the end of the pipeline" node. Where the lossless sibling works on files already on disk, WriteXMPMetadataTensor eats the actual IMAGE tensor coming out of your sampler or img2img pass, writes your metadata into it, and saves the result to disk. If you want every fresh generation to automatically carry tags like portrait, sunset so your library organizes itself from birth, drop this at the end of the graph and skip the manual tagging step entirely.
Mechanically it's a re-save plus an ExifTool call. The node takes the first frame of the batch, converts the tensor to a PIL image, saves it out in your chosen format, then shells out to ExifTool to stamp the XMP tags. Because PIL has to re-encode the image, this is where the README's big red warning lives: the tensor node re-formats the image, and anything that was in the file before gets purged before the new metadata is written. The code tries to be polite - it reads the standard XMP fields off your input_image_path before saving and rewrites what it can - but the ComfyUI workflow embedded in a generated PNG, or any exotic chunk, is gone. If you care about provenance (and with AI images you usually do, since the embedded workflow is how you share and reproduce), save first and use the lossless node instead.
Inputs:
image- the tensor from any image-producing node.metadata- tags as comma-separated text or JSON, default1girl, black hair.format_mode-Preserve formatneedsinput_image_pathso it can copy the original extension;Smart formatpicks PNG when the image has an alpha channel, JPEG when a "photo-like" heuristic (high color variance, lots of unique colors) fires, and PNG otherwise;Force PNGandForce JPGjust do what they say.metadata_type-Subject(tags),Description, orCustom XMP(the field name goes incustom_metadata).write_mode-Add to existingmerges with tags it read back from the source file,Replace allwipes and starts fresh,Delete specifiedremoves the listed tags from what was already there.
Optional but worth knowing: input_image_path (the original file, used for the output name and format reference) and output_directory (default ./tagged).
Output is output_path, a STRING telling you where the tagged file landed. Output node, so the graph finalizes on it.
Gotchas, in order of how often they bite:
- The metadata purge. This is the one to remember: only the lossless node keeps existing metadata. Use the tensor node and the embedded workflow on your PNG is gone.
- First frame only. A batch of four images means only
image[0]gets tagged and saved; the rest are ignored. - Where the file goes. With no
input_image_path, the default./taggedresolves relative to ComfyUI's working directory (fallback: ataggedfolder inside the pack). Feed itinput_image_pathand it drops the output next to your original in ataggedsubfolder instead, keeping the original filename. - ExifTool missing returns an error string. Windows gets the bundled
exiftool.exe; Linux/macOS users install ExifTool themselves (apt install libimage-exiftool-perl,brew install exiftool).
Install is the shared pack route: ComfyUI Manager search "comfyui-too-xmp-metadata", or git clone https://github.com/tetsuoo-online/comfyui-too-xmp-metadata into ComfyUI/custom_nodes and restart. Python deps are pillow and numpy, both already in ComfyUI.
It's the most feature-dense node of the three - format modes, write modes, custom fields - and also the one to treat with a little caution, purely because of what it wipes on the way out. Great for a final save-and-tag step on images you don't need to reproduce; for your already-important library, the lossless node is the safer play.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| metadata | STRING | 1girl, black hair | — |
| format_mode | COMBO | 4 options: Preserve format, Smart format, Force PNG, Force JPG | |
| metadata_type | COMBO | Subject | 3 options: Subject, Description, Custom XMP |
| write_mode | COMBO | Add to existing | 3 options: Add to existing, Replace all, Delete specified |
| custom_metadataopt | STRING | — | |
| input_image_pathopt | STRING | — | |
| output_directoryopt | STRING | ./tagged | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| output_path | STRING | — |