Nodes/comfyui_nakagawa/Load Image (Base64)
ComfyUI Node

Load Image (Base64)

Paste the picture into the graph instead of uploading a file

By nakagawadev·Created about a year ago·Updated about 24 hours ago· 2
Load Image (Base64)
    • IMAGE
    • MASK
    ◄data►

    ComfyUI's front door for images is a file. LoadImage reads a filename out of ComfyUI/input/, which is why dragging a picture into the UI quietly copies it there. Great for a human with a screenshot on their desktop, miserable for a program.

    Driving ComfyUI over the HTTP API makes that painful: multipart-upload the bytes to /upload/image, get a filename back, hope the server resolved it the same way you meant, then reference that name inside the graph JSON. Two round trips and a file sitting in input/ that nothing ever deletes. Load Image (Base64) deletes the dance. The image is the input - a base64 string - so one POST to /prompt carries the pixels and the workflow together.

    That's the pack's personality: nakagawadev/comfyui_nakagawa is three API-oriented nodes, two of which stream video out over the websocket instead of writing to temp/. This one is the same instinct on the read side - keep the pixels on the wire, out of the filesystem.

    How it works

    There's one required widget, data, a big multiline STRING. The author's tooltip says it takes "Base64 image, raw or as a data:image/...;base64, URI" - and it genuinely takes both. Under the hood the node splits on the first comma and decodes whatever's left, so a data URI prefix is dropped cleanly and a bare iVBORw0KGgo… decodes just as well.

    Then it's a short, sensible pipeline: PIL opens the bytes, ImageOps.exif_transpose applies any EXIF orientation tag (so a phone photo doesn't arrive sideways - a detail plenty of loaders get wrong), it converts to RGB, and normalizes to the float 0–1 tensor ComfyUI expects.

    The two outputs are exactly what LoadImage gives you:

    • IMAGE → anything that eats an image: VAE Encode, a ControlNet preprocessor, an img2img chain, an upscaler.
    • MASK → the alpha channel when there is one, and all ones when there isn't.

    That last bit is the sharp edge. A mask of ones is not "no mask" - it's a fully white mask, i.e. everything selected. If you hand it to an inpaint conditioning node expecting a hole, you'll get the opposite of what you wanted. Only wire MASK when you know your source has alpha.

    Using it in the UI

    The node ships a small frontend extension (web/load_image_base64.js) that adds a live preview of whatever's in the textarea and a load image as base64 button that opens a file picker and dumps the chosen file into data as a data URI. Both are client-side only - nothing uploaded, nothing written to disk - and both are declared serialize: false, so an exported API workflow contains data and nothing else. The preview is a preview, not part of the graph.

    For API clients, the node appears as a normal node in the prompt JSON:

    "3": {
      "class_type": "LoadImageBase64Nak",
      "inputs": { "data": "iVBORw0KGgoAAAANSUhEUg..." }
    }
    

    To get a string to paste when you're testing by hand:

    python -c "import base64;print(base64.b64encode(open('photo.png','rb').read()).decode())"
    

    Installing it

    ComfyUI Manager, search comfyui_nakagawa, or by hand:

    cd ComfyUI/custom_nodes
    git clone https://github.com/nakagawadev/comfyui_nakagawa
    # then restart ComfyUI
    

    The nodes show up under api/image and api/video. No model downloads, and nothing to pip install on the pack's side - pyproject.toml has an empty dependencies list. Don't read that as "portable", though: nodes.py imports av, torch, numpy, PIL, folder_paths, and from comfy_api.util import VideoCodec, VideoContainer at module scope, and all of it runs on import for every node in the pack, including this one. Torch and PIL you already have; PyAV ships with ComfyUI for its own video nodes (if yours somehow lacks it, pip install av into ComfyUI's environment). comfy_api.util is the one that bites - on a ComfyUI old enough to predate it, the pack fails to import as a whole and this node silently never appears in the menu, despite having nothing to do with video. Also note requires-python = ">=3.10".

    Where people get burned

    The node just isn't in the list. Almost always the import failure above, or the classic "I cloned it into a subfolder of custom_nodes" mistake. Check your console for the traceback before reinstalling anything.

    Malformed base64. Newlines and stray whitespace are fine - Python's decoder discards characters outside the alphabet. What isn't fine is a truncated string, or a data URI missing its comma (it splits on the first comma and decodes the rest), either of which throws binascii.Error in your console.

    Payload size. The base64 blob lives inside the workflow JSON, so it's serialized with the graph: save the workflow and you've saved the image inside it, roughly a third larger than the file. Phone JPEGs are fine; a 4K PNG is not, and every queued run re-ships the whole string. If it's big and you'll run it repeatedly, upload it once and use LoadImage.

    A dead preview. The preview and the button are frontend JS, and the frontend is exactly the surface ComfyUI's Nodes 2.0 rewrite keeps breaking across packs. If the preview stops rendering but generation still works, that's the extension, not the node - the Python side is untouchable by that churn.

    Should you use it

    If the image already exists as bytes in your app - a user's upload, a canvas, a previous API response - yes, this is the right node and there's no reason to touch the disk. If you're a person with a file and a mouse, LoadImage is still less hassle. The node's real audience is one line of code that doesn't want a multipart form.

    Categoryapi/image

    Inputs (1)

    NameTypeDefaultDescription
    dataSTRINGBase64 image, raw or as a data:image/...;base64, URI

    Outputs (2)

    NameTypeDescription
    IMAGEIMAGE—
    MASKMASK—