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

> Discover how ComfyUI embeds workflow serialization metadata directly into PNG files using text chunks. Learn how images become portable workflow artifacts.

- Repository: [Comfy Org/ComfyUI](https://github.com/Comfy-Org/ComfyUI)
- Tags: internals
- Published: 2026-02-26

---

**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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/nodes.py) demonstrates how to manually construct PNG metadata with embedded workflow data:

```python
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:

```python
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:

```python
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`](https://github.com/Comfy-Org/ComfyUI/blob/main/comfy_execution/graph_utils.py), creating a portable representation of the entire generation process.
- **Metadata embedding** uses Pillow's `PngInfo` class in [`nodes.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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`](https://github.com/Comfy-Org/ComfyUI/blob/main/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.