| name | comfyui-character |
| description | Produce consistent character images and reference-driven edits inside ComfyUI. Use this Skill for scenarios 1–4 from `comfyui-studio`'s trigger index: 1=生图 (selfie from text), 2=模仿 (mimic a reference image), 3=改图 (Flux.2 Klein single-image edit), 4=融合 (Flux.2 Klein dual-image fuse). Ships four preset workflow templates: `workflows/selfie-text-to-image.json`, `workflows/selfie-mimicry.json`, `workflows/flux2-klein-image-edit.json`, `workflows/flux2-klein-image-edit-dual.json`. |
ComfyUI Character
Four preset workflows for character-consistent selfies (with a user-trained LoRA) and
reference-image-driven edits (with Flux.2 Klein). The Plugin does not distribute any
character LoRA, face embedding, voice sample, or other private identity asset. Every character
this Skill produces is one the user trained themselves and dropped into ComfyUI's standard
models/loras/ directory.
The 4 trigger scenarios
These are scenarios 1–4 from comfyui-studio's index. The number is the trigger, the workflow
is the implementation.
| # | Trigger | User says | Preset workflow | Model stack at a glance |
|---|
| 1 | 生图 (generate) | "用我的角色画一张自拍" | workflows/selfie-text-to-image.json | Z-Image base + 2 LoRAs (face + style) |
| 2 | 模仿 (mimic) | "照着这张照片再画一张同款" | workflows/selfie-mimicry.json | ZIT-flavoured Z-Image finetune + 2 LoRAs + Z-Image Fun ControlNet + Qwen3.5 vision LLM |
| 3 | 改图 (edit) | "把背景换成办公室" | workflows/flux2-klein-image-edit.json | Flux.2 Klein 9B + Qwen3 8B + bundled VAE |
| 4 | 融合 (fuse) | "把这张图里的人物放到那张图里" | workflows/flux2-klein-image-edit-dual.json | same as #3, with 2x LoadImage inputs |
Scenarios 1 and 2 share the same two LoRA placeholders (your_face_lora.safetensors for
the character's face, your_style_lora.safetensors for the artistic style). Scenarios 3 and 4
do not use LoRAs at all — the character / object identity is carried by the reference image
itself, not by a LoRA.
Why scenarios 1 and 2 use different checkpoints
The two selfie presets use different checkpoints for a reason: the direct selfie is the
"default look" (Z-Image base, 8 steps, the canonical character), while the mimicry preset is a
"reference-driven" workflow that needs a ZIT-flavoured Z-Image finetune plus a ControlNet and a
local vision LLM to do the imitation. They are not interchangeable — if you want scenario 1
(direct generation), use the Z-Image base; if you want scenario 2 (mimicry), use the ZIT
finetune.
Scenario 1 — 生图 (generate) — selfie-text-to-image.json
Pipeline: Checkpoint → LoraLoaderModelOnly (face) → LoraLoaderModelOnly (style) → KSampler.
Models
| Field | Value (as written in the JSON) | Where it lives on disk |
|---|
CheckpointLoaderSimple.ckpt_name | Z-Image-Base-8steps-豹豹喵呜の白玉v2White_Marble-AIO_v2-bf16.safetensors | models/checkpoints/ |
LoraLoaderModelOnly[0].lora_name (face) | your_face_lora.safetensors (placeholder — replace with your file) | models/loras/ |
LoraLoaderModelOnly[1].lora_name (style) | your_style_lora.safetensors (placeholder — replace with your file) | models/loras/ |
| CLIP / VAE | bundled with the CheckpointLoader (outputs […, 1] and […, 2]) | — |
Submit
- Place the two LoRAs under
<ComfyUI>/models/loras/. Edit the two
LoraLoaderModelOnly.lora_name fields in the JSON to point at your filenames if they differ
from the placeholders.
- Compose the 4-module prompt (see
references/prompt-patterns.md):
<trigger word>, <identity block>, <outfit block>, <pose + scene block>
Pass via --prompt on the CLI; the __PROMPT__ marker in the workflow gets replaced.
- Submit via the Python script or the MCP server (see
comfyui-workflow/SKILL.md).
Knobs
| Knob | Default | What it does | When to change it |
|---|
LoraLoaderModelOnly[0].strength_model (face) | 1.0 | How strongly the face LoRA controls the model | Lower to 0.7 if the character overpowers the scene |
LoraLoaderModelOnly[1].strength_model (style) | 1.0 | How strongly the style LoRA controls the model | Lower to 0.7 if the style is too dominant |
KSampler.steps | 8 | Distilled-model default; raise to 20+ for a non-distilled base | — |
KSampler.cfg | 1.5 | Distilled-model default; raise to 6 for a non-distilled base | — |
Scenario 2 — 模仿 (mimic) — selfie-mimicry.json
Pipeline: LoadImage (reference) → AIO_Preprocessor → ControlNet application →
UNETLoader (ZIT-flavoured finetune) → KSampler → VAEDecode → SaveImage, with two
LoraLoaderModelOnly nodes (face + style) and a llama_cpp_instruct_adv node that runs the
vision LLM on the reference to produce the positive prompt.
This workflow does not use IP-Adapter. It uses a different mechanism: a ControlNet is fed
the reference image's preprocessed output, and a vision LLM (Qwen3.5) looks at the reference
and produces a text description that becomes the positive prompt. The two LoRAs then keep the
character identity stable.
Models
| Field | Value (as written in the JSON) | Where it lives on disk |
|---|
UNETLoader.unet_name | ZIT-moodyPornMix_zitV10R1DPO_fp16.safetensors (ZIT-flavoured Z-Image finetune) | models/unet/ (or wherever your UNETLoader looks) |
ControlNetLoader.control_net_name | Z-Image-Fun-Controlnet-Union-2.1.safetensors | models/controlnet/ |
LoraLoaderModelOnly[0].lora_name (face) | your_face_lora.safetensors (placeholder — replace) | models/loras/ |
LoraLoaderModelOnly[1].lora_name (style) | your_style_lora.safetensors (placeholder — replace) | models/loras/ |
llama_cpp_instruct_adv.ckpt_name (vision LLM) | Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf | models/llama/ (or wherever the llama.cpp loader looks) |
LoadImage.image | __IMAGE1__ (replace at submit time with --filename) | input/ |
Custom nodes required
The selfie-mimicry.json workflow depends on several custom nodes that ship outside the default
ComfyUI install. Without these, submission will fail with missing_node_type.
| Custom node | Used for | Install |
|---|
comfyui_controlnet_aux (or any AIO_Preprocessor + ZImageFunControlnet provider) | ControlNet preprocessing and application | https://github.com/Fannovel16/comfyui_controlnet_aux |
ComfyUI-Manager + the ModelPatchLoader / ZImageFunControlnet nodes | Loading the Z-Image Fun ControlNet union | any pack that ships ModelPatchLoader |
ComfyUI-LLaMA-CPP (or a llama.cpp loader for ComfyUI) | Loading Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf for vision reprompting | https://github.com/daniel-lewis-ab/ComfyUI-LLaMA-CPP (or compatible) |
rgthree-comfy (or compatible Any Switch / Image Comparer pack) | The Any Switch (rgthree) nodes in the original workflow | https://github.com/rgthree/rgthree-comfy |
ComfyUI-Impact-Pack (or any LayerUtility: ImageScaleByAspectRatio V2 provider) | Aspect-ratio-aware image scaling | https://github.com/ltdrdata/ComfyUI-Impact-Pack |
Submit
- Install the custom nodes above.
- Place the model files under the standard ComfyUI directories.
- Drop a reference image into the ComfyUI input folder and pass its filename with
--filename on the CLI (the __IMAGE1__ marker in the workflow's LoadImage.image field
gets replaced). Or edit the LoadImage.image field directly in the JSON.
- Set the trigger word via
--trigger (the __TRIGGER__ marker in the CR Text node
gets replaced).
- Submit via the Python script or the MCP server. The script will poll until the run
finishes and download the saved image from
outputs/.
The 4-module prompt (for the vision-LLM-reprompted positive prompt)
For scenario 2 the prompt is produced by the vision LLM, not by the user. The user provides
only the trigger word (via --trigger). The vision LLM fills in the rest of the
description from the reference image. See references/prompt-patterns.md for the rules of
thumb on what the trigger word should and shouldn't include.
Scenario 3 — 改图 (edit) — flux2-klein-image-edit.json
Pipeline: LoadImage (reference) → Flux.2 Klein UNET + CLIP + VAE → KSampler → VAEDecode →
SaveImage. One LoadImage input (__IMAGE1__).
Models
| Field | Value (as written in the JSON) | Where it lives on disk |
|---|
UNETLoader.unet_name | flux-2-klein-base-9b-fp8.safetensors | models/unet/ |
DualCLIPLoader.clip_name1 | qwen_3_8b_fp8mixed.safetensors | models/clip/ |
DualCLIPLoader.clip_name2 | diffusion_pytorch_model.safetensors | models/clip/ |
VAELoader.vae_name | bundled with the UNET, or the user's preferred Flux.2 Klein VAE | models/vae/ |
LoadImage.image | __IMAGE1__ (replace at submit time with --filename) | input/ |
Submit
- Place the reference image into the ComfyUI input folder and pass its filename with
--filename on the CLI.
- Pass the edit prompt with
--prompt. The prompt describes what to change (e.g.
"the same person sitting in an office chair, professional lighting"). The reference image
supplies the unchanged parts.
- Submit via the Python script or the MCP server.
Knobs
| Knob | Default | What it does | When to change it |
|---|
KSampler.steps | 20 | Standard distilled-Klein default | Raise to 30+ for higher quality at the cost of latency |
KSampler.cfg | 1.0 | Distilled-Klein default | Usually leave alone |
KSampler.denoise | 0.75 | How much the prompt is allowed to change the reference | Lower to 0.4–0.5 for "subtle" edits; raise to 0.9+ for "full re-imagination" |
Scenario 4 — 融合 (fuse) — flux2-klein-image-edit-dual.json
Same model stack as scenario 3, but with two LoadImage inputs (__IMAGE1__ and
__IMAGE2__). The semantic is "the subject of image 1 in the scene of image 2, with the
prompt describing the rest" — e.g. "the person from image 1, now wearing the outfit from
image 2, in the office of image 2".
Models
Identical to scenario 3, with one extra LoadImage input. Replace __IMAGE1__ with
--filename and __IMAGE2__ with --filename2 at submit time.
Submit
- Place both reference images into the ComfyUI input folder.
- Pass filenames with
--filename (image 1, the subject) and --filename2 (image 2,
the scene / outfit).
- Pass the fusion prompt with
--prompt describing how to combine them.
- Submit via the Python script or the MCP server.
Knobs
Same as scenario 3, plus an extra ReferenceLatent strength control (default 0.85) that
governs how much of image 2 the result should inherit.
Why most "consistent character" attempts fail (scenarios 1 and 2)
A LoRA is a strong prior on identity, but it is not magic. Three things derail consistency:
- Inconsistent training data. Mix of angles, lighting, outfits, and stylizations. A LoRA
trained on a single photo or on stylistically inconsistent reference images will not
generalize. See
references/lora-guide.md for the data-prep checklist.
- Weak prompt structure. Putting the LoRA trigger word in a wall of adjectives loses the
signal. The LoRA trigger must lead the prompt and the prompt must avoid words that fight it.
- Wrong sampler / scheduler / CFG for the base model. Some samplers oversmooth identity
features. See
references/prompt-patterns.md for the rules of thumb.
LoRAs are required for scenarios 1 and 2
Both selfie workflows' LoraLoaderModelOnly nodes reference placeholder LoRA filenames
(your_face_lora.safetensors and your_style_lora.safetensors). If those files do not exist
on disk, ComfyUI will fail at submission. To swap in a different LoRA, edit the lora_name
field in the workflow JSON to point at your file. The LoRAs are user identity assets; the
Plugin does not bundle them and does not name anyone's private LoRAs.
What this Skill does not do
- It does not run LoRA training. Training is its own project and has its own tooling (Kohya,
OneTrainer, ai-toolkit). The Skill only consumes a LoRA the user already has.
- It does not bundle or distribute any LoRA, model, face embedding, or voice sample. Every
identity asset is user-supplied.
- It does not invent characters. The user must define who the character is.
Requirements
- ComfyUI running locally (see
comfyui-workflow/SKILL.md for the transport).
- For scenarios 1 and 2: a face LoRA and a style LoRA in
models/loras/. The workflow JSONs
reference them as your_face_lora.safetensors and your_style_lora.safetensors.
- For scenario 2: the custom-node pack list above. Plus a ZIT-flavoured Z-Image finetune, a
Z-Image Fun ControlNet, and a Qwen3.5 GGUF vision LLM.
- For scenarios 3 and 4: the Flux.2 Klein 9B UNET, the Qwen3 8B CLIP, and the bundled VAE.
License
Apache-2.0. See LICENSE.