SamplerCustomAdvanced
The custom sampler that makes Flux follow your doodle (and monkeypatches Flux to do it)
- noise
- guider
- sampler
- sigmas
- latent_image
- output
- denoised_output
This is the heart of the ComfyUI-SC-PhotoDoodle pack, the node the whole thing is built around. It's ComfyUI's own SamplerCustomAdvanced - the node for hand-rolling a custom sampling pass - with two extra inputs bolted on: condition_image and use_clone_pe. Feed it a VAE-encoded doodle and it steers Flux toward your sketch.
The PhotoDoodle idea, briefly: instead of prompting Flux to edit a photo, you draw the edit. Sketch a black stroke over the bear, write "Add a black stroke to the bear on the cloud. by sccloudeffect", and the model honors both. The doodle's latent tokens get concatenated onto the image tokens inside the Flux transformer, so generation is literally conditioned on your drawing. This pack is a reimplementation of the showlab PhotoDoodle research project, adapted into native ComfyUI nodes.
How it works is worth understanding, because it's also why this pack can bite you. The class subclasses SamplerCustomAdvanced and inherits its standard plumbing: noise, guider, sampler, sigmas, latent_image in; output and denoised_output out. On top of that it adds:
condition_image(LATENT) - a VAE-encoded image. In the example workflow that's your doodle run through PhotoDoodleCrop, then VAEEncode.use_clone_pe(BOOLEAN) - set it totrue; the README says so outright, and the example workflow does. It's the difference between "good" and "optimal".
During sampling the node stuffs both values into the guider's model_options and calls guider.sample. But the actual magic lives in a prestartup script: every time ComfyUI launches, this pack replaces comfy.ldm.flux.model.Flux with a DoodleFlux subclass, globally. That subclass reads condition_image out of model_options, concatenates the doodle's tokens onto the image's tokens, and - when use_clone_pe is on - clones the positional encoding so the doodle tokens get the same position IDs as the canvas. That "clone PE" is the trick the flag is named after, and it's why the README insists on true.
The rest of the inputs are the standard custom-sampler stack, wired exactly like the example workflow: noise from RandomNoise, guider from BasicGuider (with FluxGuidance at 3.5), sampler from KSamplerSelect (euler), sigmas from BasicScheduler (normal, 20 steps), and latent_image from an EmptySD3LatentImage at your target size. Outputs are output (the sampled LATENT, into VAEDecode → SaveImage) and denoised_output (the last-step prediction - mostly a debugging aid).
Two things you need to know before running it. First, this is Flux-only. The patch only touches comfy.ldm.flux.model.Flux; hook this into an SDXL checkpoint and condition_image does nothing. The example uses flux1-dev-fp8.safetensors - the 12B Dev model at fp8, which is how consumer cards fit it - plus two LoRAs: pretrain.safetensors (the base PhotoDoodle LoRA) and one of the effect LoRAs (sksedgeeffect, sksmagiceffects, skspaintingeffects, sksmonstercalledlulu) from nicolaus-huang/PhotoDoodle on Hugging Face, linked in the README. The effect trigger goes at the very end of the prompt.
Install is the shared pack story - search ComfyUI-SC-PhotoDoodle in ComfyUI Manager, or
cd ComfyUI/custom_nodes
git clone https://github.com/latentcat/ComfyUI-SC-PhotoDoodle
then restart. There's no requirements.txt; the code's imports (torch, einops, PIL, numpy) are already in ComfyUI's environment. What you actually need are the model files above.
Where people get burned:
- It's a global monkeypatch. Installing this pack replaces Flux for every Flux workflow on your machine, not just doodle graphs. When it works it's transparent - no
condition_imagemeans it falls through to normal Flux - but if a ComfyUI update changes Flux's internals, this patch is what breaks, and it takes your plain Flux gens down with it. If all your Flux output suddenly fails after an update, suspect this pack before anything else. - The size mismatch trap. If the doodle you VAE-encode isn't the same dimensions as your empty latent, conditioning silently degrades instead of erroring. That's precisely what PhotoDoodleCrop exists to prevent - use it.
- Forgot the trigger word. No
by <trigger>at the end of the prompt, no doodle effect. Easy to miss, obvious in hindsight. - It's not the core SamplerCustomAdvanced. Same base class, but the two extra inputs only exist on this node, so swap it into the graph rather than trying to wire a doodle into the built-in one.
- The README's usage section mentions a "PhotoDoodleEdit" node that doesn't exist in this pack - the editing happens here, in the sampler.
One honest caveat: this pack has essentially zero footprint in the community - the README and the two example workflows (photodoodlev1.json, photodoodle_speedv1.json) are your documentation. The speed workflow is worth a look if you want the TeaCache-accelerated variant. Expect to be your own support.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| noise | NOISE | — | |
| guider | GUIDER | — | |
| sampler | SAMPLER | — | |
| sigmas | SIGMAS | — | |
| latent_image | LATENT | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| output | LATENT | — |
| denoised_output | LATENT | — |