Nodes/was-node-suite-comfyui/Custom Sampler Affine Advanced
ComfyUI Node Runs on cloud

Custom Sampler Affine Advanced

The affine for people who already build their own sampler stack

By WASasquatch·Created 4 years ago·Updated about 9 hours ago· 1,884
Custom Sampler Affine Advanced
  • noise
  • guider
  • sampler
  • sigmas
  • latent_image
  • affine_schedule
  • external_mask
  • affine_options
  • output
  • denoised_output
  • mask
◄affine_interval1►
◄max_scale1.020►
◄max_bias0.000►
◄patternwhite_noise►
◄affine_seed0►
◄affine_seed_incrementfalse►
◄temporal_modestatic►
◄affine_streamsvideo►
◄affine_spacelatent►
◄debugfalse►
◄affine_acts_oncontent►
◄affine_keep_colouroff►

Three nodes in this pack do the same transform. KSampler Affine Advanced is the one-node version. Affine Sampler hands you back a wrapped SAMPLER to drop into an existing custom-sampling graph. This one - Custom Sampler Affine Advanced - replaces SamplerCustomAdvanced itself, taking the noise, guider, sampler and sigmas you already built and denoising with the affine running inside the loop. If your graph has a guider you care about, or noise you chose deliberately, this is the node.

That last part is the actual reason to be here. The affine family is a texture/detail injector: it scales the latent and adds an offset through a mask while the sampler runs, mid-run where detail gets established. The stock way to get similar texture is a node that leaves extra noise behind during sampling, and it works - but the only real control is "how much". Affine gives you a mask (26 patterns, from grain to perlin to sobel edges read off the latent), a schedule, and a spatial gate. And it doesn't restart the sampler to do it, so multistep history, noise sequences and packed audio/video latents all survive untouched.

The reason it's worth combining with your own noise: the pack also ships Temporal Noise Hold and Affine Pattern Noise, both of which are NOISE sources. Put one of those on this node's noise socket and you're shaping the starting draw and the trajectory in the same graph, which is the closest thing here to a proper video-texture pipeline.

What goes in

noise, guider, sampler, sigmas, latent_image - the standard five. sigmas is worth a note: the affine curve is read over the sigmas you feed it, so the step count is one fewer than the number of entries. Get that wrong and your carefully designed schedule lands on the wrong steps.

Then the affine block, identical to its sibling nodes: affine_interval (apply every Nth step; 1 = every step), max_scale (what the latent is multiplied by at the peak - 1.0 is nothing, 1.02 is visible texture over a 20-step run, 1.05 is strong, past 1.08 the image breaks up), max_bias (added beside the scale, not through it - 0.005 is a drift, 0.02 shifts colour), pattern, affine_seed, affine_seed_increment and temporal_mode.

temporal_mode is the video one: static, per_frame, or drift (slid by the drift_speed/drift_angle_deg/drift_renew values on Affine Options). Content-aware patterns ignore it.

Optional inputs: affine_schedule (from Affine Schedule), affine_options (from Affine Options - and its pattern beats the widget here), external_mask, affine_streams (video, audio, both for packed latents), affine_space (latent to match the Latent Affine node's scale, model for the sampler's internal one), debug (logs step, sigma, strength, resolved scale and bias - genuinely the fastest way to find out whether anything is happening), and affine_acts_on.

Careful with that last one. content multiplies only the picture the model has resolved; latent multiplies the whole thing including the sampler's own noise, which prints as grain fixed in the frame on a model that holds high noise for most of its run. Default is content. Change it deliberately.

Three outputs: output (the latent the sampler ended on), denoised_output (the model's own estimate of the clean latent at the last step), and mask - the mask the last application ran through, at latent resolution, all zero if nothing was applied. That mask output is the debugger nobody uses. Wire it to a Preview Mask node and look at it: if your grain isn't landing where you thought, you'll see why in one render instead of five.

Limits worth knowing before you fight them

Four stock samplers can't carry the affine: dpm_fast, dpm_adaptive, uni_pc and uni_pc_bh2. They drive their own solver or rescale the latent before every model call, so there's no clean step boundary to hook. They don't crash; the console says so and they sample normally. If you ignored that line, you'll conclude the feature is broken.

Install

Ships in WAS Node Suite v3. Manager, search WAS Node Suite v3, install, restart. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was-node-suite-comfyui.git

Needs ComfyUI 0.14.0+ and Python 3.10+. No packages get installed, now or later - the v3 rewrite dropped every default dependency, so if you're following a v2-era install guide that has you fighting pinned OpenCV or InsightFace, you're reading about a different version of this pack. Optional groups live behind features: keys in <ComfyUI user dir>/was-node-suite/config.yaml, written on first start; nothing relevant to sampling is off by default.

One more thing if you're porting an old graph: this node has no legacy equivalent. It's new with v3, and any 2024 workflow that used WAS sampling nodes will be wired to something else entirely.

CategoryWAS Suite/Sampling

Inputs (20)

NameTypeDefaultDescription
noiseNOISEWhere the starting noise comes from.
guiderGUIDERWhat steers the denoising.
samplerSAMPLERThe sampler to wrap, from KSamplerSelect or any other SAMPLER source. Every stock sampler carries the affine except dpm_fast, dpm_adaptive, uni_pc and uni_pc_bh2, which is reported in the console rather than failing the run.
sigmasSIGMASThe noise levels to step through. The affine curve is read over these, so one entry fewer than the length is the step count.
latent_imageLATENTThe latent to denoise: an image, a video, or MiniMax H3 audio and video. A video-only H3 latent is sampled beside held silence and comes back video only.
affine_intervalINT11–100Apply on every Nth step of the schedule. 1 = every step, 4 = every fourth, which leaves the sampler more room to settle between applications.
max_scaleFLOAT1.0200–2What the latent is multiplied by at the schedule's peak, compounding over every step it lands on: below 1.0 = softer; 1.0 = no change; 1.02 = visible texture over a 20 step run; 1.05 = strong; past 1.08 = breaks up.
max_biasFLOAT0.000-2–2What is added at the peak of the schedule, beside max_scale rather than through it. 0.0 = nothing, 0.005 = a gentle drift, 0.02 shifts the whole colour. bias_field on Affine Options decides whether that is one flat offset or a noise field.
patternCOMBOwhite_noiseWhich mask the affine lands through. 'solid' = everywhere; 'white_noise' and the coloured noises = grain of varying coarseness; 'perlin', 'checker', 'bayer', 'worley_edges' and the rest = shapes; 'detail_region', 'smooth_region', 'edges_sobel', 'edges_laplacian' = read off the latent; 'external_mask' = the mask wired in.
affine_seedINT00–18446744073709550000Seeds 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.
affine_seed_incrementBOOLEANfalse`true` advances the seed on every application, so the grain moves from step to step. `false` holds one mask for the whole run, which keeps the affine landing in the same places.
temporal_modeCOMBOstaticVideo latents only: 'static' = one mask on every frame; 'per_frame' = a new mask each frame; 'drift' = one mask sliding across the frame, set by drift_speed, drift_angle_deg and drift_renew on Affine Options. Content-aware patterns ignore it.
affine_scheduleoptDICTThe per-step strength curve from an Affine Schedule node. Left unwired the affine ramps up over the middle of the run, from a fifth of the way in to four fifths.
affine_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.
affine_spaceoptCOMBOlatentWhich latent max_scale and max_bias are measured against. 'latent' = the same scale Latent Affine uses, so a value means the same thing in both places; 'model' = the sampler's own internal latent, which some models hold at a very different magnitude.
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.
debugoptBOOLEANfalse`true` logs every application to the console: the step, its sigma, the strength, the resolved scale and bias, and which streams were touched. `false` stays quiet.
affine_acts_onoptCOMBOcontentWhat max_scale multiplies: 'content' = only the picture the model has resolved; 'latent' = the whole latent, noise included; 'noise' = only the noise, which the model turns into extra detail. Scaled noise can print as fixed grain and shift colour.
affine_keep_colouroptCOMBOoffWhat colour max_scale is kept from shifting: 'off' = none; 'average' = each latent channel's average stays put, so no overall cast; 'regional' = the broad colour and shading stay put too, and only fine detail is scaled. max_bias is added either way.

Outputs (3)

NameTypeDescription
outputLATENTThe latent the sampler ended on.
denoised_outputLATENTThe model's own estimate of the clean latent at the last step.
maskMASKThe mask the last application ran through, at latent resolution. All zero where no affine was applied.