JoyAI_Image_LATENTS
Where your reference image becomes 16-channel latents
- image
- vae
- latent
Think of this as the pack's "prepare the canvas" node. It takes an image, resizes it to the resolution you want to generate at, encodes it into latents with the Wan 2.1 VAE, and hands the whole bundle to JoyAI_Image_SM_KSampler. If you're doing the pack's headline trick - loading a photo and telling the model "Turn the plate blue" - this is the node right after LoadImage and right before the sampler.
The reason it's fatter than the boring old VAEEncode is that JoyAI-Image is a unified model: it wants the reference image as latent and as pixel-space context that the diffusion transformer can attend to. So the latent dict this node outputs carries not just samples, but also width, height, and the resized images list. The sampler unpacks all of that when it runs. It's a pipe, not just an encoder.
The inputs you'll actually touch
image- the reference. This is required, which is the giveaway that this pack is built for editing/reference workflows first and text-to-image second.width/height- default 1024 each, step 32. This is also your aspect-ratio control: the input image is bilinear-upscaled to exactly these dimensions before encoding, so set them to the output size you want. Cropping is not part of the deal.seed- seeds the noise generator for the latent. Same seed + same everything else = same result. Leave it alone on first runs and change it when you want variety.vae(optional) - feed it the output ofJoyAI_Image_SM_VAE. Here's the gotcha: if you pass an image, the VAE is not optional. The node raises"When use image,you must provide a vae"if you forget. Only when you run without an image can the VAE stay disconnected.
How the encoding works
Under the hood it uses the WanxVAE - JoyAI-Image is, architecturally, a heavily retrained Wan 2.1-class DiT with a Qwen3-VL text encoder, and it kept Wan's VAE and its 16-channel latent space. That's a bigger latent than SDXL's 4 channels, which is a big part of why this model eats VRAM. The node seeds a torch generator from your seed and builds the latents at 16 channels, one frame. Nothing subtle here - it's VAE encode plus a resize, wrapped so the sampler gets exactly the context it expects.
Where it goes
LoadImage → JoyAI_Image_LATENTS → JoyAI_Image_SM_KSampler → JoyAI_Vae_Decoder → SaveImage
The DECODER on the end is the pack's own node too, because the Wan VAE isn't one of ComfyUI's stock loaders. And since the reference image rides along in the latent dict, the sampler can do proper image-conditioned generation without you re-loading the image on the sampler side.
Install
Same pack install as every node here:
cd ComfyUI/custom_nodes
git clone https://github.com/smthemex/ComfyUI_JoyAI_Image
cd ComfyUI_JoyAI_Image
pip install -r requirements.txt
then restart ComfyUI (or use Manager and search "ComfyUI_JoyAI_Image"). You must have Wan2.1_VAE.pth in ComfyUI/models/vae - it's the VAE both this node and the DECODER rely on. The requirements file pins transformers>=4.57,<4.58 and demands flash-attn>=2.8.0, so budget for a possibly slow install on Windows.
Where people get burned
The most common failure here is the resolution mismatch. The node happily upscales a tiny input to 1024×1024, and then the model sees a stretched photo - spatial relationships (JoyAI-Image's whole selling point) get distorted. Keep width/height close to the input's aspect ratio, and remember this isn't an upscaler: generate, then upscale separately if you need a bigger canvas.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| seed | INT | 00–2147483647 | — |
| width | INT | 1024256–16384 | — |
| height | INT | 1024256–16384 | — |
| vaeopt | VAE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | — |