Ideogram 4 Quad Keyframe
Four reference images, one canvas, and a mask that decides what gets redrawn
- image_1
- image_2
- image_3
- image_4
- mask_1
- mask_2
- mask_3
- mask_4
- canvas
- noise_mask
Ideogram 4 is the weirdest good model you'll run this year. It's prompted with structured JSON, it only ships quantized checkpoints, and its license is non-commercial - but it remembers characters better than any open model without a LoRA. This pack builds a 2×2 "quad" storyboard canvas around that: four panels, each one a frame of your scene, each independently locked to a reference image, soft-regenerated, or inpainted. Ideogram4QuadKeyframe is the compositing heart of the whole thing - the node that turns up to four reference images plus a set of rules into one canvas and one mask.
How it works
You feed it up to four reference images (image_1 through image_4), then for each of the four panels - top-left, top-right, bottom-left, bottom-right - you say three things:
<panel>_image- which loaded image sits in that cell, ornone. The same image can be reused in multiple panels, which is how you keep one character's panel consistent with the next.<panel>_denoise-0means the panel is a frozen keyframe (the reference pixels are kept),>0means the panel is a "soft seed": the reference is treated as a starting point and gently regenerated. A cell with no image just generates from scratch.<panel>_mask_mode-ignorefor a uniform denoise across the whole cell,denoise_maskedfor classic inpaint (only the region you painted regenerates, the rest stays frozen), ordenoise_unmaskedfor the inverse.
Mechanically it's elegant. The node paints the canvas with a background color (background is gray/black/white), crops each reference into its cell with fit set to crop or stretch, then writes a per-pixel noise mask where 0 = keep the reference and 1 = regenerate. That per-panel denoise value literally becomes the mask value, so the sampler does the rest. mask_feather blurs the mask edges so panel boundaries don't show up as hard seams, and lock_gutters freezes the gutter/margin strips so the panels never bleed into each other.
The inputs and outputs that actually matter
The geometry block - width, height, frame_width, frame_height, gutter, margin - defines the canvas and cell layout. Two things worth knowing: keep everything a multiple of 16 (Ideogram 4's native resolutions are), and if you're also using this pack's Layout node, wire the geometry from it so every node agrees.
The two outputs are the whole point:
canvas→ feed intoVAEEncodenoise_mask→ feed intoSetLatentNoiseMask
Then run your sampler at full denoise - the mask, not the denoise slider, is what controls how much each panel changes.
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/reality-comes/comfyui-ideogram4-quad-storyboard
Restart ComfyUI and the nodes appear under image/ideogram4 (ComfyUI Manager can also find the pack by searching "Ideogram 4 Quad Storyboard"). There are no extra Python dependencies - the requirements file is deliberately empty, and it uses ComfyUI's bundled torch and comfy. The heavy lifting is the model itself: you need an Ideogram 4 checkpoint (ideogram-ai/ideogram-4-fp8 or -nf4), and the nf4 variant is the one that fits a single 24GB card.
Where people get burned
The classic failure is geometry drift: the Keyframe, the Split node, and the Keyframe Release node all take the same six numbers, and if you edit them in one place and not the others, your panels come out misaligned or your release rects land in the gutter. Set them once in Ideogram4QuadLayout and wire the ints everywhere.
Second gotcha: don't expect a denoise of 0.5 on one panel to look like img2img at 0.5 - it's the mask that carries the value, so keep the sampler's own denoise at 1.0. And if your inpainted panel comes out with a visible seam, that's what mask_feather is for. A few pixels is usually enough.
Inputs (30)
| Name | Type | Default | Description |
|---|---|---|---|
| width | INT | 265616–16384 | — |
| height | INT | 150416–16384 | — |
| frame_width | INT | 128016–16384 | — |
| frame_height | INT | 70416–16384 | — |
| gutter | INT | 320–1024 | — |
| margin | INT | 320–1024 | — |
| background | COMBO | 3 options: gray, black, white | |
| fit | COMBO | 2 options: crop, stretch | |
| mask_feather | INT | 00–256 | — |
| lock_gutters | BOOLEAN | false | — |
| top_left_image | COMBO | none | 5 options: none, image_1, image_2, image_3, image_4 |
| top_left_denoise | FLOAT | 0.000–1 | — |
| top_left_mask_mode | COMBO | 3 options: ignore, denoise_masked, denoise_unmasked | |
| top_right_image | COMBO | none | 5 options: none, image_1, image_2, image_3, image_4 |
| top_right_denoise | FLOAT | 0.000–1 | — |
| top_right_mask_mode | COMBO | 3 options: ignore, denoise_masked, denoise_unmasked | |
| bottom_left_image | COMBO | none | 5 options: none, image_1, image_2, image_3, image_4 |
| bottom_left_denoise | FLOAT | 0.000–1 | — |
| bottom_left_mask_mode | COMBO | 3 options: ignore, denoise_masked, denoise_unmasked | |
| bottom_right_image | COMBO | none | 5 options: none, image_1, image_2, image_3, image_4 |
| bottom_right_denoise | FLOAT | 0.000–1 | — |
| bottom_right_mask_mode | COMBO | 3 options: ignore, denoise_masked, denoise_unmasked | |
| image_1opt | IMAGE | — | |
| image_2opt | IMAGE | — | |
| image_3opt | IMAGE | — | |
| image_4opt | IMAGE | — | |
| mask_1opt | MASK | — | |
| mask_2opt | MASK | — | |
| mask_3opt | MASK | — | |
| mask_4opt | MASK | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| canvas | IMAGE | — |
| noise_mask | MASK | — |