Nodes/ComfyUI-EditUtils/EditUtils: Flux3Klein Edit Text Encode lrzjason
ComfyUI Node

EditUtils: Flux3Klein Edit Text Encode lrzjason

One Node, Three Images, One FLUX.3 Prompt

By lrzjason·Created 9 months ago·Updated 3 days ago· 256
EditUtils: Flux3Klein Edit Text Encode lrzjason
  • clip
  • vae
  • image1
  • image2
  • image3
  • mask
  • conditioning
  • latent
  • custom_output
  • main_image
  • mask
◄prompt—►
◄ref_longest_edge1024►
◄ref_longest_edge21024►
◄ref_longest_edge31024►

The EditUtils pack has two ways to feed a model an image. One is a config chain - a preparer node per image, chained together, then a generic encoder. The other is the "simple path": a single encoder node with an image socket on it. Flux3KleinEditTextEncode_EditUtils is the FLUX.3 version of the simple path, and it's the one to use when your graph is one image in, one edited image out.

Three image inputs, one prompt, five outputs. That's it.

What it does under the hood

It's a thin wrapper that builds the standard FLUX.3 config for you and calls the generic encoder. image1 becomes the main image - its latent is the sampling start latent and its pixels come back out as main_image. image2 and image3 are plain references. All three get ref_crop = "pad" and lanczos upscaling, and each has its own longest-edge number.

The model_config it passes along is the flux3klein route with vae_unit = 16 and an empty instruction template. That 16 matters: the FLUX.3 VAE is the FLUX.2 packed autoencoder - 32 latent channels, 8x downscale, 2x2 patchify - so one latent token covers 16 pixels and references get padded to 16-pixel multiples. The text side is a Qwen3-VL encoder whose stacked hidden states feed a much wider conditioning vector than FLUX.2 klein's, and the tokenizer pads the text window to a multiple of 80 on its own. None of that is your problem - the point is that 1024 is a token-grid number, not a magic resolution.

Inputs and outputs

Required: clip, vae, and prompt (multiline). Optional: image1, image2, image3, ref_longest_edge, ref_longest_edge2, ref_longest_edge3 (all default 1024), and mask.

Five outputs, in order: conditioning → your sampler's positive, latent → the sampler's latent, custom_output → the full dict of intermediates for the extractor, main_image → the prepared main image, mask.

One hard constraint: at least one image. The node raises "At least one image must be provided" if you connect none, so it isn't a text-to-image node - for FLUX.3 layout generation with no reference you want the config chain plus a bbox node, which carries a table-only entry into the prompt.

The catch: no bounding boxes here

This is the thing to understand before you wire it up, because it's the difference between "FLUX.3 ignores my layout" and a working graph.

The simple path has no element table. Boxes are built by Flux3KleinBBox_EditUtils, which decorates entries in a config chain, and merged into the prompt by the generic EditTextEncode_EditUtils. So if you want FLUX.3's signature generation-by-layout - draw a box, describe what belongs in it - you have to use the chain. Flux3KleinEditTextEncode_EditUtils is for the plain case: one reference, one instruction, no boxes.

The pack's own example graph uses the generic encoder precisely because it draws a box.

The graph it slots into

UNETLoader (flux3_image_klein_sd_int8_convrot) ──────────────► KSampler ──► VAEDecode ──► SaveImage
CLIPLoader  (qwen3vl_4b_fp8_scaled, type flux3) ──┐
VAELoader   (flux2-vae) ──────────────────────────┴──► Flux3KleinEditTextEncode
LoadImage ────────────────────────────────────────────────────┘
Flux3KleinEditTextEncode ── conditioning ──► KSampler positive
                          ── latent ──────► KSampler latent
                          ── conditioning ──► ConditioningZeroOut ──► KSampler negative

Sampling for the step-distilled checkpoint: euler / simple, about 8 steps, CFG 1.0, denoise 1.0, fixed seed. It's a flow-map model trained to sample without guidance in the 4–16 step range, so no negative prompt. If you're on the base checkpoint instead, CFG comes back into play.

Install

Manager → search ComfyUI-EditUtils → install → restart, or:

cd ComfyUI/custom_nodes
git clone https://github.com/lrzjason/ComfyUI-EditUtils

No pip dependencies. Everything heavy is model-side and you need three files, which people routinely get half-right: the UNET, qwen3vl_4b_fp8_scaled.safetensors loaded through CLIPLoader with type flux3 (not qwen_image, not krea2 - the type selects the tokenizer), and flux2-vae.safetensors. Plus a ComfyUI build that actually implements FLUX.3, since the pack defers to core rather than shipping the architecture.

Where people get burned

Writing a prompt and nothing else. The shipped FLUX.3 example has an empty prompt widget, which invites you to do the same. Don't. The guidance is to write a full caption of the result with the edit folded in; the model is reading your text, not guessing.

Treating the longest-edge numbers as quality settings. They're token-grid sizes. Pushing all three to 4096 doesn't make the references sharper, it makes the sequence longer and the graph slower.

Assuming you can attach a table by adding a node downstream. You can't - boxes have to be on the config the encoder reads, which means the chain path. That's the one design boundary worth remembering: simple path, no boxes; chain path, boxes.

Categoryadvanced/conditioning

Inputs (10)

NameTypeDefaultDescription
clipCLIP—
vaeVAE—
promptSTRING—
image1optIMAGE—
image2optIMAGE—
image3optIMAGE—
ref_longest_edgeoptINT102416–4096Longest edge of the main image latent grid
ref_longest_edge2optINT102416–4096Longest edge of reference 2
ref_longest_edge3optINT102416–4096Longest edge of reference 3
maskoptMASK—

Outputs (5)

NameTypeDescription
conditioningCONDITIONING—
latentLATENT—
custom_outputANY—
main_imageIMAGE—
maskMASK—