Comfy for Advertising - OOH Visualization
About
This ComfyUI workflow helps brand and creative teams quickly place an existing poster into realistic Out-of-Home (OOH) scenarios. It’s organized in two clear stages. STEP 1 – POSTER REFORMAT takes your source poster and non-destructively reformats it to multiple aspect ratios (vertical, horizontal, square, or custom) while preserving the original design. This stage is driven by LoadImage, PrimitiveNode (for width/height values), PrimitiveBoolean (to enable/disable formats), ComfySwitchNode (to route only the formats you want), and BatchImagesNode (to package multiple outputs).
SaveImage captures clean, reformatted versions ready for placement.
STEP 2 – OOH VISUALIZATION composites a chosen reformatted poster into a real or described environment. You can supply a location photo with LoadImage via the IMAGE ENVIRONMENT group or describe a scene in plain language (for example, "OOH billboard in downtown San Francisco, on top of a two-story building, pedestrian eye level") in the PROMPT ENVIRONMENT group. GeminiImage2Node is used as a prompt helper to convert your plain-language description into structured scene notes and file-name context.
Two custom processing nodes (ae42e662-5e2b-4f52-b31d-267bd68f1c6f and 88b611c3-ce43-4e7a-a59f-dd16f57a8853) handle the perspective mapping and blending of the poster onto the target surface, so you can fine-tune scale, skew, and overlay strength for believable placement. SaveImage then outputs the final mockups, optionally in batches, so you can compare formats and scenes side by side.
What you provide
- Reading the workflow
Runs in ComfyUI. This workflow can't run on this page. Open it in Comfy Cloud with everything loaded, or download it for your own ComfyUI.
Try in CloudTry an example
Inside the workflow
A read-only preview of the node graph. Open it in Comfy Cloud or download it to edit the nodes.
Loading the graph
Open full-size workflow preview- Author
- ComfyUI
Requires custom nodes
- Reading the workflow
Use this workflow from code
Generated from the workflow graph and inputs when possible; stored examples are shown otherwise. Review credentials and endpoints before running code, which may incur charges.
What you need
- Runs on
- Comfy Cloud
- Key
- COMFY_API_KEY
- Your files
- Uploaded by the code from their urls
Needs a paid Cloud plan and credits.
# Python 3.10+: pip install comfy-sdk==0.4.0
import os
from comfy_sdk import Comfy
api_key = os.environ.get("COMFY_API_KEY", "").strip()
if not api_key: raise RuntimeError("Set COMFY_API_KEY")
os.environ.setdefault("COMFY_BASE_URL", "https://cloud.comfy.org")
client = Comfy(api_key=api_key)
workflow = client.workflows.from_json({
"64": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "Poster_Resize_Custom",
"images": [
"252:251",
0
]
},
"_meta": {
"title": "Save Image"
}
},
"66": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_FILENAME"
},
"_meta": {
"title": "INPUT POSTER"
}
},
"127": {
"class_type": "GeminiImage2Node",
"inputs": {
"aspect_ratio": "1:1",
"images": [
"128",
0
],
"model": "gemini-3-pro-image-preview",
"prompt": "visualize this poster (keep its aspect ratio) to a OOH billboard in the street of downtown San Francisco, on top of a two story building. Shot from the perspective of a pedestrian",
"resolution": "2K",
"response_modalities": "IMAGE+TEXT",
"seed": 0,
"system_prompt": "You are an expert image-generation engine. You must ALWAYS produce an image.\nInterpret all user input—regardless of format, intent, or abstraction—as literal visual directives for image composition.\nIf a prompt is conversational or lacks specific visual details, you must creatively invent a concrete visual scenario that depicts the concept.\nPrioritize generating the visual representation above any text, formatting, or conversational requests."
},
"_meta": {
"title": "Nano Banana Pro (Google Gemini Image)"
}
},
"128": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_2_FILENAME"
},
"_meta": {
"title": "INPUT POSTER"
}
},
"129": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "Poster_Mockup",
"images": [
"136",
0
]
},
"_meta": {
"title": "Save Image"
}
},
"135": {
"class_type": "BatchImagesNode",
"inputs": {
"images.image0": [
"142",
0
],
"images.image1": [
"235:162",
0
]
},
"_meta": {
"title": "Batch Images"
}
},
"136": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"127",
0
],
"on_true": [
"138",
0
],
"switch": [
"141",
0
]
},
"_meta": {
"title": "If/Else Switch"
}
},
"138": {
"class_type": "GeminiImage2Node",
"inputs": {
"aspect_ratio": "auto",
"images": [
"135",
0
],
"model": "gemini-3-pro-image-preview",
"prompt": "replace the billboard of the first image to the poster of the second image",
"resolution": "2K",
"response_modalities": "IMAGE+TEXT",
"seed": 303461698342474,
"system_prompt": "You are an expert image-generation engine. You must ALWAYS produce an image.\nInterpret all user input—regardless of format, intent, or abstraction—as literal visual directives for image composition.\nIf a prompt is conversational or lacks specific visual details, you must creatively invent a concrete visual scenario that depicts the concept.\nPrioritize generating the visual representation above any text, formatting, or conversational requests."
},
"_meta": {
"title": "Nano Banana Pro (Google Gemini Image)"
}
},
"141": {
"class_type": "PrimitiveBoolean",
"inputs": {
"value": True
},
"_meta": {
"title": "SWITCH TO IMAGE ENVIRONMENT"
}
},
"142": {
"class_type": "LoadImage",
"inputs": {
"image": "UPLOADED_image_3_FILENAME"
},
"_meta": {
"title": "IMAGE"
}
},
"243": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "Poster_Resize1",
"images": [
"252:250",
0
]
},
"_meta": {
"title": "Save Image"
}
},
"245": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "Poster_Resize2",
"images": [
"252:249",
0
]
},
"_meta": {
"title": "Save Image"
}
},
"247": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "Poster_Resize3",
"images": [
"252:248",
0
]
},
"_meta": {
"title": "Save Image"
}
},
"235:152": {
"class_type": "Paste By Mask",
"inputs": {
"image_base": [
"235:230",
0
],
"image_to_paste": [
"235:229",
0
],
"mask": [
"235:229",
0
],
"resize_behavior": "keep_ratio_fit"
},
"_meta": {
"title": "Paste By Mask"
}
},
"235:153": {
"class_type": "ImageToMask",
"inputs": {
"channel": "red",
"image": [
"235:152",
0
]
},
"_meta": {
"title": "Convert Image to Mask"
}
},
"235:158": {
"class_type": "ImageToMask",
"inputs": {
"channel": "red",
"image": [
"235:231",
0
]
},
"_meta": {
"title": "Convert Image to Mask"
}
},
"235:159": {
"class_type": "InvertMask",
"inputs": {
"mask": [
"235:153",
0
]
},
"_meta": {
"title": "Invert Mask"
}
},
"235:162": {
"class_type": "ImageRemoveAlpha+",
"inputs": {
"image": [
"235:165",
0
]
},
"_meta": {
"title": "🔧 Image Remove Alpha"
}
},
"235:163": {
"class_type": "Get Image Size",
"inputs": {
"image": [
"128",
0
]
},
"_meta": {
"title": "Get Image Size"
}
},
"235:165": {
"class_type": "Paste By Mask",
"inputs": {
"image_base": [
"235:231",
0
],
"image_to_paste": [
"128",
0
],
"mask": [
"235:152",
0
],
"resize_behavior": "keep_ratio_fit"
},
"_meta": {
"title": "Paste By Mask"
}
},
"235:168": {
"class_type": "MaskComposite",
"inputs": {
"destination": [
"235:158",
0
],
"operation": "add",
"source": [
"235:159",
0
],
"x": 0,
"y": 0
},
"_meta": {
"title": "Combine Masks"
}
},
"235:191": {
"class_type": "FluxResolutionNode",
"inputs": {
"aspect_ratio": "1:1 (Perfect Square)",
"custom_aspect_ratio": "1:1",
"custom_ratio": False,
"divisible_by": "8",
"megapixel": "2.1"
},
"_meta": {
"title": "Flux Resolution Calc"
}
},
"235:229": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#ffffff",
"custom_height": [
"235:163",
1
],
"custom_width": [
"235:163",
0
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"235:230": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#000000",
"custom_height": [
"235:191",
1
],
"custom_width": [
"235:191",
0
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"235:231": {
"class_type": "LayerUtility: ColorImage V2",
"inputs": {
"color": "#ffffff",
"custom_height": [
"235:191",
1
],
"custom_width": [
"235:191",
0
],
"size": "custom"
},
"_meta": {
"title": "LayerUtility: ColorImage V2"
}
},
"252:131": {
"class_type": "StringConcatenate",
"inputs": {
"delimiter": "",
"string_a": "Resize the OOH poster to ",
"string_b": "3:1"
},
"_meta": {
"title": "Concatenate Text"
}
},
"252:132": {
"class_type": "StringConcatenate",
"inputs": {
"delimiter": "",
"string_a": [
"252:131",
0
],
"string_b": " aspect ratio. Only move and rescale elements to fit the new format. Maintain all original typography, colors, graphics, logos, and visual hierarchy exactly as they are. No design changes."
},
"_meta": {
"title": "Concatenate Text"
}
},
"252:248": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"66",
0
],
"on_true": [
"252:260",
0
],
"switch": True
},
"_meta": {
"title": "If/Else Switch"
}
},
"252:249": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"66",
0
],
"on_true": [
"252:259",
0
],
"switch": True
},
"_meta": {
"title": "If/Else Switch"
}
},
"252:250": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"66",
0
],
"on_true": [
"252:258",
0
],
"switch": True
},
"_meta": {
"title": "If/Else Switch"
}
},
"252:251": {
"class_type": "ComfySwitchNode",
"inputs": {
"on_false": [
"66",
0
],
"on_true": [
"252:264",
0
],
"switch": True
},
"_meta": {
"title": "If/Else Switch"
}
},
"252:258": {
"class_type": "GeminiNanoBanana2",
"inputs": {
"aspect_ratio": "9:16",
"images": [
"66",
0
],
"model": "Nano Banana 2 (Gemini 3.1 Flash Image)",
"prompt": "Resize this OOH poster to the new aspect ratio. Only move and rescale elements to fit the new format. Maintain all original typography, colors, graphics, logos, and visual hierarchy exactly as they are. No design changes.",
"resolution": "2K",
"response_modalities": "IMAGE",
"seed": 806105455324093,
"system_prompt": "You are an expert image-generation engine. You must ALWAYS produce an image.\nInterpret all user input—regardless of format, intent, or abstraction—as literal visual directives for image composition.\nIf a prompt is conversational or lacks specific visual details, you must creatively invent a concrete visual scenario that depicts the concept.\nPrioritize generating the visual representation above any text, formatting, or conversational requests.",
"thinking_level": "HIGH"
},
"_meta": {
"title": "Nano Banana 2"
}
},
"252:259": {
"class_type": "GeminiNanoBanana2",
"inputs": {
"aspect_ratio": "16:9",
"images": [
"66",
0
],
"model": "Nano Banana 2 (Gemini 3.1 Flash Image)",
"prompt": "Resize this OOH poster to the new aspect ratio. Only move and rescale elements to fit the new format. Maintain all original typography, colors, graphics, logos, and visual hierarchy exactly as they are. No design changes.",
"resolution": "2K",
"response_modalities": "IMAGE",
"seed": 952769964755599,
"system_prompt": "You are an expert image-generation engine. You must ALWAYS produce an image.\nInterpret all user input—regardless of format, intent, or abstraction—as literal visual directives for image composition.\nIf a prompt is conversational or lacks specific visual details, you must creatively invent a concrete visual scenario that depicts the concept.\nPrioritize generating the visual representation above any text, formatting, or conversational requests.",
"thinking_level": "HIGH"
},
"_meta": {
"title": "Nano Banana 2"
}
},
"252:260": {
"class_type": "GeminiNanoBanana2",
"inputs": {
"aspect_ratio": "1:1",
"images": [
"66",
0
],
"model": "Nano Banana 2 (Gemini 3.1 Flash Image)",
"prompt": "Resize this OOH poster to the new aspect ratio. Only move and rescale elements to fit the new format. Maintain all original typography, colors, graphics, logos, and visual hierarchy exactly as they are. No design changes.",
"resolution": "2K",
"response_modalities": "IMAGE",
"seed": 521333244527813,
"system_prompt": "You are an expert image-generation engine. You must ALWAYS produce an image.\nInterpret all user input—regardless of format, intent, or abstraction—as literal visual directives for image composition.\nIf a prompt is conversational or lacks specific visual details, you must creatively invent a concrete visual scenario that depicts the concept.\nPrioritize generating the visual representation above any text, formatting, or conversational requests.",
"thinking_level": "HIGH"
},
"_meta": {
"title": "Nano Banana 2"
}
},
"252:264": {
"class_type": "GeminiNanoBanana2",
"inputs": {
"aspect_ratio": "auto",
"images": [
"66",
0
],
"model": "Nano Banana 2 (Gemini 3.1 Flash Image)",
"prompt": [
"252:132",
0
],
"resolution": "2K",
"response_modalities": "IMAGE",
"seed": 188565245405213,
"system_prompt": "You are an expert image-generation engine. You must ALWAYS produce an image.\nInterpret all user input—regardless of format, intent, or abstraction—as literal visual directives for image composition.\nIf a prompt is conversational or lacks specific visual details, you must creatively invent a concrete visual scenario that depicts the concept.\nPrioritize generating the visual representation above any text, formatting, or conversational requests.",
"thinking_level": "HIGH"
},
"_meta": {
"title": "Nano Banana 2"
}
}
})
# Replace each example.com URL with a public URL of your file.
workflow.set_input("66", "image", client.assets.from_url("https://example.com/replace-with-a-url/image"))
workflow.set_input("128", "image", client.assets.from_url("https://example.com/replace-with-a-url/image_2"))
workflow.set_input("142", "image", client.assets.from_url("https://example.com/replace-with-a-url/image_3"))
# run() also passes api_key to the partner nodes in this workflow.
job = client.run(workflow, api_key=api_key)Questions & answers
You can do either. Use IMAGE ENVIRONMENT with LoadImage to supply a real location photo, or use PROMPT ENVIRONMENT if you want GeminiImage2Node to turn your plain-language description into scene notes and file-name context. You can switch between paths with the provided switches.
STEP 1 reformats by resizing and padding/cropping according to the PrimitiveNode dimensions you set, driven by toggles in PrimitiveBoolean and routing via ComfySwitchNode. This preserves your design while producing standard OOH-friendly canvases (vertical, horizontal, square, or custom).
They handle the OOH placement step: warping the selected poster to match the billboard plane and blending it into the scene. Use their UI controls to fine-tune perspective (corner/scale/rotation) and the blend for a realistic result.
Yes. Enable the formats you want with the PrimitiveBoolean switches in STEP 1. Then, in STEP 2, connect those outputs to your chosen environment path. BatchImagesNode and SaveImage will export a consistent set of mockups in one run.