ComfyUI-ApplySpectralLock
Apply Spectral Lock (Flux/USDU) — a ComfyUI model patch that performs a Latent Spectral Handoff on 16-channel Flux latents during sampling. It uses two decoupled controls:
- Structure lock — low frequencies are anchored to a reference latent (
latent_orig). Active from step 0, so composition / color / lighting are preserved even at 0.8 denoise. - Detail gate (variance matching) — instead of blanket dampening, the high frequencies are renormalized to match the reference's per-channel contrast/energy. This keeps real, crisp detail while stopping the Rectified Flow solver from overshooting into digital grain ("Turbo paradox"). The gate follows a schedule-aware cosine (or linear) envelope, engaging mostly over the final steps.
Requirements
- ComfyUI (recent master)
- PyTorch
- Flux-family models with 16 latent channels (SD 4-channel latents are passed through unchanged)
Install
Clone into ComfyUI/custom_nodes/:
ComfyUI/custom_nodes/ComfyUI-ApplySpectralLock/
Restart ComfyUI.
Usage
- Load your MODEL as usual.
- Provide
latent_orig: the full-resolution (or reference) latent corresponding to the uncorrupted base structure you want to lock to (image2latent / VAE encode of the guide image). - Run ApplySpectralLock → connect
patched_modelinto your sampler / Ultimate SD Upscale chain before sampling.
Parameters
| Input | Role |
|---|---|
kernel_size |
Odd kernel size for separable Gaussian low-pass (default 15). |
blur_sigma |
Gaussian sigma for the 1D kernels (default 3.0). |
alpha_lock |
Structure lock: blend weight for anchoring low frequencies to latent_orig (0–1, default 0.85). Active from step 0. |
gamma_max |
Detail gate: maximum strength of high-frequency variance matching toward the reference (0–1, default 0.70). Higher = texture energy pulled harder toward the original (less grain). |
decay_type |
cosine (default, slow start then firm asymptote) or linear progress scaling for the detail gate. |
edge_taper |
Spatial edge-gating width in latent cells (default 48, 0 disables). The spectral adjustment is cosine-tapered to zero at the tile border so USDU seam blending sees no discontinuity. Auto-clamped to half the tile size. |
Ultimate SD Upscale (USDU)
For tiled runs, the patcher estimates the tile’s (y, x) offset in latent space via normalized template matching on channel-mean maps, then crops latent_orig once per tile and caches the blurred reference low-pass for the rest of that tile’s steps.
Note: Very large Gaussian radii relative to USDU overlap can still stress seams; keep kernel_size coherent with your tile overlap.
Flux CFG = 1.0
The hook is registered with disable_cfg1_optimization=True so the post-CFG callback still runs when CFG is disabled (common for distilled / single-stream Flux runs).
Implementation notes
- Depthwise separable Gaussian: two
F.conv2dpasses withgroups=16,F.pad(..., mode='reflect')before each pass. - HF variance matching: per-channel spatial std of the reference high-band is cached once per tile; the generated high-band is rescaled by
std_orig / (std_t + eps)and blended in via the cosine gate. A gain clamp prevents runaway amplification on flat patches. - Covariance-preserving (joint) epsilon: the denominator floor is tied to the mean energy across the 16 channels (
eps_abs + eps_rel * mean_c(std_t)), not to each channel in isolation. Near-flat channels inherit the joint floor instead of being independently amplified, keeping inter-channel scale ratios stable and preventing microscopic color/tonal drift (and division-by-zero on studio backdrops). - Spatial edge-gating: a cached cosine taper mask zeroes the spectral delta
(X_new - denoised)at tile borders (edge_taper), so USDU seam blending never reconciles a discontinuity introduced here. - Decoupled envelopes: structure lock (
alpha) is applied every step; the variance gate (gamma) ramps with schedule progress. - Float32 for blur / variance / NCC internals; outputs are cast back to
denoised.dtypeand device. - Sigma progress: schedule-agnostic
progress = 1 - sigma / max_sigma_seenfor the current tile/run.
License
MIT (replace if you use a different license for your repo).