Workflow Serialization in ComfyUI: How Metadata Gets Embedded in PNG Files

ComfyUI embeds complete workflow metadata directly into PNG files as standard text chunks (tEXt) using Pillow's PngInfo object, storing the serialized node graph as JSON under the "prompt" key and any custom extra_pnginfo as additional text entries, enabling images to function as self-contained, portable workflow artifacts.

The Comfy-Org/ComfyUI repository implements a sophisticated workflow serialization system that captures the entire node graph state and embeds it directly into output PNG files. This technical architecture ensures that every generated image carries its complete generation recipe, allowing users to reconstruct, modify, or rerun the exact workflow that produced the visual output by simply loading the image file.

The Three-Stage Metadata Embedding Process

ComfyUI's metadata embedding operates through a precise three-stage pipeline that transforms the active node graph into embedded PNG text chunks.

Stage 1: Serializing the Workflow Graph

The serialization process begins in comfy_execution/graph_utils.py, where every node in the active workflow converts itself into a JSON representation via the Node.serialize() method. This method captures the node's class type, its input links, and an optional override_display_id parameter.

The GraphBuilder.finalize() method then aggregates these individual node serializations into a complete graph structure. This finalized JSON representation becomes the workflow prompt—a complete description of the node graph that the server passes back to the client as the prompt field in execution results.

Stage 2: Creating PNG Metadata with PngInfo

When a SaveImage or SaveImageWebsocket node executes, the actual metadata embedding occurs in nodes.py within the save_images() method. The implementation uses Pillow's PngInfo class to construct the metadata container:

  1. Workflow Prompt: The method adds the serialized graph JSON under the "prompt" key using metadata.add_text("prompt", json.dumps(prompt)).
  2. Custom Metadata: If the workflow includes extra_pnginfo (a hidden field containing custom data such as workflow-wide IDs or version tags), the method iterates over these entries and adds each as a separate text chunk: metadata.add_text(key, json.dumps(value)).

The PngInfo object is then passed to Image.save() along with the output path and compression settings: img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=self.compress_level).

Stage 3: Reading Metadata from Saved Images

The retrieval process occurs in server.py within the image-view endpoint. When ComfyUI serves a previously saved PNG, it uses Pillow to open the image and access the text attribute, which exposes the embedded tEXt chunks as a dictionary.

The server reconstructs the workflow by extracting the "prompt" value and parsing it as JSON, restoring the original node graph structure. This enables the UI to offer options to rerun or edit the workflow directly from the image file.

Key Implementation Files and Functions

Understanding the specific source files involved helps developers extend or debug the metadata system:

  • comfy_execution/graph_utils.py: Contains Node.serialize() and GraphBuilder.finalize() which convert the active node graph into JSON format suitable for embedding.
  • nodes.py: Houses SaveImage.save_images() which constructs the PngInfo object, adds the "prompt" and extra_pnginfo text chunks, and writes the final PNG file.
  • comfy_api/latest/_ui.py: Implements _create_png_metadata() for API-based PNG exports and animated PNG handling, using the same metadata structure as the standard save nodes.
  • server.py: Handles metadata extraction via the image serving endpoints, reading the text attribute from Pillow Image objects to retrieve the embedded workflow JSON.

Practical Code Examples

Saving Images with Embedded Workflow Metadata

The internal implementation in nodes.py demonstrates how to manually construct PNG metadata with embedded workflow data:

from PIL import Image, PngImagePlugin
import json
import os

def save_with_metadata(img, prompt_dict, extra_pnginfo, output_path, filename):
    # Create PngInfo container

    metadata = PngImagePlugin.PngInfo()
    
    # Add the serialized workflow prompt

    metadata.add_text("prompt", json.dumps(prompt_dict))
    
    # Add any custom extra_pnginfo fields

    if extra_pnginfo:
        for key, value in extra_pnginfo.items():
            metadata.add_text(key, json.dumps(value))
    
    # Save with embedded metadata

    full_path = os.path.join(output_path, filename)
    img.save(full_path, pnginfo=metadata, compress_level=4)
    return full_path

Extracting Workflow Data from Existing PNGs

To retrieve the embedded workflow from a ComfyUI-generated PNG:

from PIL import Image
import json

def extract_workflow(png_path):
    with Image.open(png_path) as img:
        # Access the text chunks dictionary

        text_chunks = img.text
        
        # Extract the workflow prompt

        workflow_json = text_chunks.get("prompt", "{}")
        workflow = json.loads(workflow_json)
        
        # Extract extra_pnginfo (everything except "prompt")

        extra_info = {
            key: json.loads(value) 
            for key, value in text_chunks.items() 
            if key != "prompt"
        }
        
        return workflow, extra_info

# Usage example

workflow, extra = extract_workflow("output.png")
print(f"Node count: {len(workflow)}")

API-Based PNG Export with Metadata

When using the ComfyUI API to generate images with embedded metadata:

import requests
import json

payload = {
    "prompt": {
        "3": {
            "inputs": {
                "text": "masterpiece, best quality",
                "clip": ["4", 0]
            },
            "class_type": "CLIPTextEncode"
        },
        # ... additional nodes

    },
    "extra_data": {
        "extra_pnginfo": {
            "workflow": {"id": "abc123", "version": "1.0"},
            "custom_tag": "experimental"
        }
    },
    "output_images": True
}

response = requests.post(
    "http://localhost:8188/api/v1/save_image", 
    json=payload
)

# The returned PNG contains both "prompt" and "workflow" text chunks

Summary

  • Workflow serialization in ComfyUI converts node graphs into JSON via Node.serialize() in comfy_execution/graph_utils.py, creating a portable representation of the entire generation process.
  • Metadata embedding uses Pillow's PngInfo class in nodes.py to write the serialized workflow under the "prompt" key and custom extra_pnginfo data as additional text chunks, storing everything as standard PNG tEXt chunks.
  • Metadata retrieval occurs in server.py when the UI reads the text attribute of Pillow Image objects, enabling the reconstruction of workflows from saved images for editing or rerunning.
  • The system supports both standard save nodes and API-based exports via comfy_api/latest/_ui.py, ensuring consistent metadata handling across all output methods.

Frequently Asked Questions

What metadata format does ComfyUI use in PNG files?

ComfyUI stores metadata as standard PNG text chunks (tEXt) using the ISO 8859-1 character set. The workflow data is serialized as JSON strings and stored under specific keys: the "prompt" key contains the complete node graph serialization, while additional keys from extra_pnginfo store custom workflow metadata. This approach ensures compatibility with standard image viewers and editing tools while maintaining human-readable workflow data.

Can I disable metadata embedding in ComfyUI outputs?

Yes, metadata embedding can be disabled by setting the disable_metadata argument to True when calling save functions. In the SaveImage.save_images() method in nodes.py, the code explicitly checks if not args.disable_metadata: before executing metadata.add_text() calls. When disabled, the PngInfo object remains empty, resulting in PNG files without embedded workflow data, which is useful for privacy-conscious deployments or when reducing file size is critical.

How do I extract the workflow from a ComfyUI-generated PNG?

To extract the embedded workflow, open the PNG file using Pillow (Image.open()), access the .text attribute to retrieve the dictionary of text chunks, and parse the "prompt" value as JSON. The text attribute exposes all embedded tEXt chunks as a Python dictionary where keys are the metadata field names and values are the JSON strings. Any additional metadata stored in extra_pnginfo will appear as separate entries in this dictionary alongside the standard "prompt" key.

What is the difference between "prompt" and "extra_pnginfo" metadata?

The "prompt" metadata contains the complete serialized node graph—the entire workflow structure including all nodes, connections, and parameters required to reproduce the generation exactly. In contrast, extra_pnginfo stores ancillary custom data injected by specific nodes or the API, such as workflow IDs, version tags, author information, or experimental parameters. While "prompt" is mandatory for workflow reconstruction, extra_pnginfo provides extensibility for applications requiring additional tracking or categorization metadata without modifying the core workflow structure.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →