Nodes/ComfyUI-GTE/Noise Rotate (gte)
ComfyUI Node

Noise Rotate (gte)

Turn the unresolved noise, don't add to it

By mgkgng·Created 2 days ago·Updated 2 days ago· 4
Noise Rotate (gte)
  • latent
  • denoised
  • model
  • latents
theta25.0
count4
variation_seed0
spread90.0
keep_parentfalse
current_sigma

You stopped a generation part-way. The image isn't there yet, but you can see it. You want to know how else it could finish. That's the question this node answers, and it answers it with an angle instead of the usual "denoise strength" slider.

How it works

A partial latent splits cleanly into two parts: what the model has already decided, and what it hasn't.

x  =  x0_pred + residual

x0_pred is the model's current guess at the finished image - the direction you picked. The residual is the noise it hasn't resolved yet, and that's where all the remaining freedom lives. Keep the prediction, turn the residual toward a different answer:

new     = randn(variation_seed) * eps.std()
eps_var = cos(theta) * eps + sin(theta) * new
variant = x0_pred + eps_var

Rotating rather than adding is the entire point, and it's the bit most "variation strength" implementations get wrong. The naive version, x + d·eps', inflates the noise level by sqrt(1+d²) - +41% at d=1 - while the scheduler still believes the old sigma. The sampler then under-denoises, and variation strength and quality loss end up tangled in one dial with no error anywhere. Because cos² + sin² = 1, this leaves ||x − x0_pred|| exactly unchanged. theta moves the direction and never the amount.

new is scaled to the residual's own magnitude rather than assuming unit variance, which is what keeps that true at any checkpoint.

The knobs

theta (default 25, range −360…360) is literally an angle: cos_sim(parent, child) = cos(theta). 0 is an exact continuation - the control the whole design rests on, returned as the original tensor. 10–45 is the useful range. 90 gives an uncorrelated direction at the same noise level. Past 90 the descendants march further from the parent while collapsing back toward each other; at 180 the variation seed stops mattering entirely and they all become one image, the negative of the continuation. Negative angles are a free extra axis - same strength, different direction.

count (1–64, default 4) - how many descendants, emitted as a batch. Pick one with Candidate Select.

variation_seed - descendant i is drawn from variation_seed + i, and the shared family direction comes from the same integer, so one number addresses the whole set.

spread (0–90, default 90) - how far apart the descendants are from each other, at the same distance from the parent. 90 fans them out maximally. 0 collapses the family to one image repeated count times, so you're paying N renders for one result. The sibling angle is capped by theta - at theta 25 they can never be more than 34.8° apart.

keep_parent - replaces the first descendant with the parent's own continuation, bit-exact, so it can be judged side by side with its alternatives instead of from memory. It replaces descendant 1 rather than adding one, so the others keep the seeds and indices they already had.

model + current_sigma are a pair, and this is where the pack earns its keep. x − x0 is the noise only for additive models (SD1.5, SDXL). Rectified flow - Flux, SD3, krea2 - builds its state as x = (1−sigma)·x0 + sigma·eps, so the same subtraction returns noise and signal; rotate that and you drag the image off the trajectory. Wire the pair and the node asks the model how it builds a noisy state: two probes of noise_scaling recover the affine map, which is then inverted, rotated, and rebuilt. There is no list of model names anywhere in the source, and there must never be one - if the map isn't affine the node refuses and says which guarantee failed. Half-wiring the pair raises, because silently falling back to the additive path is the bug.

Inputs and outputs

Required: latent (the sampler's output - the state still carrying its noise), denoised (that same sampler's denoised_output; it's what gets preserved), then theta/count/variation_seed/spread/keep_parent. Optional: model, current_sigma. One output: latents, a batch, at the same noise level. It goes to Candidate Select, or straight to a continuation sampler.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/mgkgng/ComfyUI-GTE

Restart; [gte] loaded 8 node(s) in the console means you're good. ComfyUI Manager: search ComfyUI-GTE. No Python deps - the pack's requirements file is empty by design - and no model files.

Common issues

"expected one latent, got a batch of 4." Choose a candidate first. Rotate operates on one parent.

"latent and denoised are identical." There's no residual left to vary - you branched at sigma 0, where every variant would be the same finished image. Branch earlier; Schedule Info tells you where the noise actually is.

Descendants that all look the same. Two failures look identical on the canvas, and the node prints which one you have. Either the residual barely matters any more (the model has committed - check the printed residual ratio), or the latents were properly separated and the model collapsed them on the way to an image. Distilled and turbo checkpoints do the second one by design: they're trained to jump to their answer and ignore the noise they're handed, which is exactly the property this node needs (see docs/knowledge/distillation.md).

euler only. Splitting a trajectory works only for samplers whose next step depends on (x, sigma) alone. dpmpp_2m degrades; dpmpp_2m_sde injects its own noise and breaks outright. heun and ddim are safe.

Keep the variation seed different from the origin seed. If they collide, the "new" noise is the tensor the candidate was born from, and the angle and noise level both quietly stop meaning what they say.

Categorygte/sampling

Inputs (9)

NameTypeDefaultDescription
latentLATENT`output` from the sampler that stopped at the checkpoint — the state still carrying its noise.
denoisedLATENT`denoised_output` from that same sampler: what the model currently believes the image is becoming. This is preserved.
thetaFLOAT25.0-360–360Angle to turn the unresolved noise — literally the cosine similarity to the original residual, cos_sim = cos(theta). 0 = exact continuation, 10-45 useful, 90 = uncorrelated (and the widest spread between descendants). Past 90 the descendants move further from the parent but back TOWARD each other; at 180 the variation seed stops mattering and they collapse to one image, the negative of the continuation. -theta mirrors +theta at equal strength.
countINT41–64How many descendants. They come out as a batch — pick one with Candidate Select.
variation_seedINT00–18446744073709490000Descendant i is drawn from variation_seed + i. The shared family direction is derived from the same number, so one integer still addresses the whole set.
spreadFLOAT90.00–90How far apart the descendants are FROM EACH OTHER, at the same distance from the parent. 90 = independent (the default, and what this node always did); 0 = every descendant identical, so `count` costs you N renders of one image. The sibling angle it produces is capped by theta: at theta 25 they can never be more than 34.8 degrees apart.
keep_parentBOOLEANfalseMake the FIRST descendant the parent's own continuation, unchanged, and vary the rest. Use it when the current state is already good: you keep it in the same batch as its alternatives, so Candidate Select compares them side by side instead of you re-running the branch to get it back. It replaces descendant 1 rather than adding one, so the others keep the seeds and indices they already had.
modeloptMODELThe model this latent came from. Wire it (with current_sigma) for anything that is not a plain additive/eps model — Flux, SD3, krea, any rectified-flow checkpoint. The node asks the model how it builds a noisy state rather than assuming.
current_sigmaoptFLOATThe sigma this latent is AT — not where the next stage ends. Wire Sigma Segment's `start_sigma` from the segment that is about to run, which is the same boundary this latent stopped on.

Outputs (1)

NameTypeDescription
latentsLATENTThe descendants, as a batch, at the same noise level as the input.