ComfyUI Node Runs on cloud

Latent Affine

The one dial that runs the whole WAS Affine pack

By WASasquatch·Created 3 years ago·Updated a day ago· 1,839
Latent Affine
  • latent
  • external_mask
  • affine_options
  • latent
  • mask
scale0.960
bias0.000
patternwhite_noise
temporal_modestatic
seed0
streamsvideo

The name AFFINE sounds like a lab report - "Adaptive Field Filtering by Intermittent Noise Enhancement" - but the actual idea is so simple you'll wonder why you didn't do it before. During diffusion, your image lives as a tensor in latent space. WAS Latent Affine lets you nudge that tensor, selectively, in the places where a mask says so. That's the entire pack, and this is the node that does it directly.

What it actually does

The transform is z' = scale * z + bias, applied only where a generated pattern mask is bright. Where the mask is 0, your latent is untouched; where it's 1, the values get scaled and shifted. The blend between those two extremes is what the mask controls, so the mask - not the scale - is the real star. You pick from 20+ patterns: spectral noise (white, pink, brown, blue, violet, green, black), procedural stuff (perlin, cross_hatch, dot_screen, ring_noise, highpass_white), geometric tiles (checker, bayer, solid), content-aware masks computed from the latent itself (detail_region, smooth_region, edges_sobel, edges_laplacian), or external_mask to feed your own image.

Why bother? Two big use cases. First, the classic two-pass workflow: run a KSampler for the high-noise steps, apply Latent Affine, then resume with a second sampler for the low-noise tail. It's the latent-space cousin of a hi-res fix second pass - same "adjust between passes" instinct, but you never leave latent space, so no VAE decode/encode round trip and none of the raster-space artifacts. Second, quality salvage: the README frames it as rescuing detail in light LoRA outputs and taming the burned, over-contrasted look that speed-boosting LoRAs (Lightning/Turbo) love to produce.

The inputs that matter

You'll set three things 95% of the time, everything else is optional tuning:

  • scale - multiplicative factor. Default 0.96. Below 1 darkens, above 1 amplifies features. "0.95 can be quite strong" is the author's own warning.
  • bias - additive offset, -2 to 2. 0.1 brightens, -0.1 darkens.
  • pattern - the mask source. Start with perlin (organic) or white_noise and get fancy later.

Then temporal_mode (static = one mask for all frames, per_frame = regenerate each frame for video), seed, and the two option inputs: options (the shared mask-tuning DICT from Latent Affine Common Options or the Super Options node) and noise_options (a per-pattern DICT that overrides the base options). If you connect an external_mask while using any procedural pattern, the generated mask gets multiplied by it - gating the effect to specific regions without giving up the pattern.

Outputs: latent (wire into your next sampler or straight to VAE Decode) and mask (the generated mask - worth previewing once so you actually see what's being affected).

Installing

This is a node in WAS Affine by WASasquatch - the same author as the legendary WAS Node Suite. In ComfyUI Manager, hit Install Custom Nodes, search "WAS Affine", install, restart. Or the manual route:

cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was_affine

Restart ComfyUI after. You need ComfyUI 1.0.0+, and the dependencies are just torch and numpy - no model downloads, no extra checkpoints. Despite the "USDU" names elsewhere in the pack, nothing here needs UltimateSDUpscale.

Where people get burned

Latent space is sensitive, and it's the whole selling point, so respect it. Start with scale near 0.95 and a soft pattern, not 1.2 and a hard checker. Some models respond far more dramatically than others - the README notes Flux/Krea-family models can show inverted behavior and may want positive scale values instead. If you see banding at mask edges, that's the mask, not the model: blur it (blur_sigma ~1.5 via options) before it ever touches the latent. And if per_frame makes video look like static noise crawl, dial the effect down before blaming the temporal mode - that liveliness is literally the point of the setting.

CategoryWAS Suite/Latent/Transform

Inputs (9)

NameTypeDefaultDescription
latentLATENTThe latent to transform. Image, video and packed audio and video latents are all handled.
scaleFLOAT0.9600–2What the latent is multiplied by where the mask is white. 1.0 = no change; 0.96 takes a little energy out, which softens; 1.2 pushes texture and contrast up.
biasFLOAT0.000-2–2What is added where the mask is white, beside scale rather than through it. 0.0 = no shift; 0.1 lifts, -0.1 drops. bias_field on Affine Options decides whether that is one flat offset or a noise field.
patternCOMBOwhite_noiseWhich mask decides where the affine lands. 'solid' covers everything; 'white_noise' and the coloured noises are grain of different coarseness; 'perlin', 'checker', 'bayer', 'cross_hatch', 'worley_edges' and the rest are shapes; 'detail_region', 'smooth_region', 'edges_sobel' and 'edges_laplacian' are read off the latent itself; 'external_mask' uses the mask wired in.
temporal_modeCOMBOstaticHow a video latent's mask varies over time. 'static' = one mask on every frame, in the same place all clip. 'per_frame' = an unrelated mask each frame. 'drift' = one mask slid across the frame, set by drift_speed, drift_angle_deg and drift_renew on Affine Options. The content-aware patterns ignore this, and so does an image latent.
seedINT00–2147483647Seeds the mask. The same seed always draws the same mask, so change it to move the grain without changing anything else. Ignored by the content-aware patterns and by 'external_mask', which read what they are given.
streamsoptCOMBOvideoWhich streams of a packed audio and video latent the affine reaches. 'video' = stream 0, 'audio' = the rest, 'both' = all of them. An ordinary latent has only a video stream, so 'audio' does nothing to it.
external_maskoptMASKA mask of your own, resized onto the latent. On pattern 'external_mask' it is the mask; on any other pattern it gates the generated one, so the affine reaches only where this is white. One mask covers every frame, or one per frame.
affine_optionsoptDICTPattern parameters and mask shaping from an Affine Options node. Leave it unwired and every value takes its default. A pattern set there wins over the pattern widget.

Outputs (2)

NameTypeDescription
latentLATENTThe transformed latent.
maskMASKThe mask the transform ran through, at latent resolution. White is where the full scale and bias landed.