Files
AEmotionStudio-ComfyUI-Shad…/shaders
Æmotion StudioandClaude Opus 5 0da8bb6e04 fix: give each latent channel its own shader draw, and stop temporal_coherent ignoring the seed
Two related defects, found while testing whether the strength ceiling scales with
latent size and step count. It does not -- SD 1.5 at a 64x64 latent and 20 steps
breaks down harder and earlier than MiniMax H3 at 38x22 and 8 steps, which
inverts the premise. The cause is the channel axis.

shaders/base.py::_expand_channels builds every extra channel as a pointwise
function (sin, abs) of a mixture of the FIRST TWO channels, so the draw spans
almost nothing however many channels are asked for. Effective rank, measured as
the participation ratio of the channel covariance spectrum:

                        4ch      16ch     24ch    128ch
    gaussian            4.00    16.00    23.90   120.30
    domain_warp         1.00     2.22     2.06     2.37
    temporal_coherent   1.00     1.00     1.00     1.00
    curl_noise          3.58    10.88    22.11    25.22
    tensor_field        3.81    14.31    23.54    90.78

domain_warp -- the default -- is a two-dimensional signal copied across up to 128
channels, and temporal_coherent is literally one channel repeated. Samplers
expect i.i.d. noise; that collapse is why the shader's own pattern surfaces so
readily. It also explains the ordering already observed on H3, where
tensor_field (near full rank) tolerated the most strength.

`decorrelate_channels` fills the channel axis from DECORRELATION_BASIS
independent renders mixed through a seeded random matrix, so the rank becomes
min(channels, basis) while every basis element is still that shader. On SD 1.5
it moves the usable ceiling from below 0.25 -- where stock is already an
abstract green smear -- to a clean photoreal variation at 0.25 that stays partly
readable at 0.5.

Guarded twice, because neither guess held on its own: generators already wider
than the basis are skipped (curl_noise spans 25 of 128 channels despite looking
correlated, so a correlation threshold was the wrong test), and the remix is
kept only when it actually widens the draw (curl_noise at four channels remixes
narrower than it started). Turning it on can never narrow the noise.

Separately, shaders/temporal_coherent_noise.py read params["base_seed"]
unconditionally where domain_warp gates it on use_temporal_coherence. The node
always sets base_seed, so that generator ignored its seed argument entirely:
every stage and every decorrelation draw produced the identical field, and only
`time` still varied. It now matches domain_warp. Nothing could select
temporal_coherent from a workflow until the previous commit, so no saved
workflow changes; the 11 legacy goldens still pass.

Exposed as an optional `decorrelate_channels` input, default off. 23 new tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 02:07:17 -07:00
..