Model Scale MOSS Image
A per-layer volume knob for a model with no tutorials
- model
- MODEL
Most people opening this node expect a merge slider and get a mixing desk instead. Model Scale MOSS Image gives you 51 independent float dials - one per weight group in a MOSS Image diffusion model - each multiplying that group's weights by anything from 0.0 to 2.0. There's no second model to blend with. It's interpolation toward nothing: 1.0 leaves a group alone, 0.0 zeroes it, above 1.0 amplifies.
So it's a merge node with one parent deleted. Which sounds useless until you remember merging is just weight arithmetic - a weighted average of existing models, minutes not days, no training data or GPU compute. This is that operation with the labels still on. It makes the node the cheapest ablation tool in ComfyUI: the way you find out which part of a checkpoint is responsible for the plastic skin, the over-strong style, or the prompt adherence that ignores half your sentence is to turn groups off and look.
Fair warning: search the big subreddits for MOSS Image and you get near-silence, so there's no crowd-tested recipe to copy. You're doing your own science.
How it works
The source doesn't hide it. scale_model_by_prefix() clones the model patcher, walks the keys under the diffusion_model. prefix, picks the dial whose name is the longest prefix of each key, and registers a weight patch:
m = model.clone()
for key, patch in m.get_key_patches(prefix).items():
scale = longest_prefix_match(key[len(prefix):], ratios)
if scale != 1.0:
m.add_patches({key: patch}, scale - 1.0, 1.0)
ComfyUI applies patches as weight * strength_model + patch * strength_patch, so passing the weight itself as the patch with strength_patch = scale - 1.0 yields exactly weight * scale. Your safetensors file is never touched, the input model isn't mutated, and an all-1.0 node adds no patches at all.
Two details worth knowing. The dial names carry a trailing dot (layers.1.) precisely so layers.1. can't swallow layers.11. in the prefix match. And granularity is per block, not per module: one layers.7. value scales that block's qkv, output projection, FFN up/gate and down, plus its norms, together. You can't isolate attention from feed-forward inside a block here.
The inputs that matter
model is the only real decision - your MOSS Image diffusion model, the file named in the pack's own key list being moss_image_sft_bf16.safetensors. The single MODEL output goes onward to your sampler, guider, or another scale node. There's nothing else on the node.
The 51 floats all default to 1.0, range 0.0–2.0, step 0.01:
layers.0.…layers.41.- the 42 main blocks. Where you'll spend all your time.text_embedder.proj.,text_embedder.blocks.0.,text_embedder.blocks.1.- the prompt path. Useful, and unusual: this architecture keeps its text embedder in the diffusion model file rather than a separate encoder, so these dials can touch it. Drop them slightly to soften adherence without touching CFG. Zeroing them isn't "unconditional," it's mush.shared_block_modulation.,video_head_modulation.,final_norm_video.,final_linear_video.,video_embedder.,time_embedding.- the plumbing around the trunk. These break loudly. If everything around the trunk zeroed and you got noise, the trunk wasn't the problem.
rope.bands is deliberately missing from the list - it's a fixed F32 constant buffer, and scaling a positional-frequency table isn't a knob, it's a bug.
Installing it
It rides along with the pack. ComfyUI Manager → search Easygoing (it's on the ComfyUI Registry, the author's recommended route), or:
cd ComfyUI/custom_nodes
git clone https://github.com/easygoing0114/ComfyUI-easygoing-nodes.git
Restart ComfyUI. No requirements.txt, no extra packages, no models beyond the checkpoint you already had - it's pure torch arithmetic on loaded weights.
The real requirement is a current ComfyUI: the whole pack is written against the V3 node API (comfy_api.latest). An older build registers none of its nodes and prints comfy_api.latest (V3 node API) not found on startup. That's an old ComfyUI, not a broken install - update ComfyUI. Relevant background: packs built this way ship zero NODE_CLASS_MAPPINGS by design, so finding no node classes in the source means nothing.
Using it without wasting an evening
Generate once with every dial at 1.0 and a fixed seed. Then set layers.0. through layers.20. to 0.0 and rerun the same seed and prompt. Don't eyeball the comparison - the same pack ships an Image Difference Checker with MAE/SSIM outputs, which is the exact job it exists for. Bisect toward whatever moved. Zeroing a chunk of blocks usually costs coherence before it costs style, which is itself the finding.
Where people get burned
It silently does nothing on the wrong key prefix. The loop only patches keys under diffusion_model., which is correct for a normally loaded MOSS Image checkpoint. Anything exposing different internal names means no prefix matches, no patches, and your model back unchanged - with zero errors and a perfectly normal-looking image. When the dials seem inert, suspect this first. The pack's Key Name Inspector is the diagnostic: it passes the model through untouched and dumps the real weight key names to a string output and the console. If those names don't look like layers.0. / text_embedder. / video_head_modulation., this node is aimed at a different architecture.
Don't test at 0.0 and write the node off. Zeroing a whole block is a violent intervention; gray soup arrives long before a subtle style shift does. Start at 0.5, one group at a time.
Inputs (52)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| video_embedder. | FLOAT | 1.000–2 | — |
| time_embedding. | FLOAT | 1.000–2 | — |
| text_embedder.proj. | FLOAT | 1.000–2 | — |
| text_embedder.blocks.0. | FLOAT | 1.000–2 | — |
| text_embedder.blocks.1. | FLOAT | 1.000–2 | — |
| shared_block_modulation. | FLOAT | 1.000–2 | — |
| layers.0. | FLOAT | 1.000–2 | — |
| layers.1. | FLOAT | 1.000–2 | — |
| layers.2. | FLOAT | 1.000–2 | — |
| layers.3. | FLOAT | 1.000–2 | — |
| layers.4. | FLOAT | 1.000–2 | — |
| layers.5. | FLOAT | 1.000–2 | — |
| layers.6. | FLOAT | 1.000–2 | — |
| layers.7. | FLOAT | 1.000–2 | — |
| layers.8. | FLOAT | 1.000–2 | — |
| layers.9. | FLOAT | 1.000–2 | — |
| layers.10. | FLOAT | 1.000–2 | — |
| layers.11. | FLOAT | 1.000–2 | — |
| layers.12. | FLOAT | 1.000–2 | — |
| layers.13. | FLOAT | 1.000–2 | — |
| layers.14. | FLOAT | 1.000–2 | — |
| layers.15. | FLOAT | 1.000–2 | — |
| layers.16. | FLOAT | 1.000–2 | — |
| layers.17. | FLOAT | 1.000–2 | — |
| layers.18. | FLOAT | 1.000–2 | — |
| layers.19. | FLOAT | 1.000–2 | — |
| layers.20. | FLOAT | 1.000–2 | — |
| layers.21. | FLOAT | 1.000–2 | — |
| layers.22. | FLOAT | 1.000–2 | — |
| layers.23. | FLOAT | 1.000–2 | — |
| layers.24. | FLOAT | 1.000–2 | — |
| layers.25. | FLOAT | 1.000–2 | — |
| layers.26. | FLOAT | 1.000–2 | — |
| layers.27. | FLOAT | 1.000–2 | — |
| layers.28. | FLOAT | 1.000–2 | — |
| layers.29. | FLOAT | 1.000–2 | — |
| layers.30. | FLOAT | 1.000–2 | — |
| layers.31. | FLOAT | 1.000–2 | — |
| layers.32. | FLOAT | 1.000–2 | — |
| layers.33. | FLOAT | 1.000–2 | — |
| layers.34. | FLOAT | 1.000–2 | — |
| layers.35. | FLOAT | 1.000–2 | — |
| layers.36. | FLOAT | 1.000–2 | — |
| layers.37. | FLOAT | 1.000–2 | — |
| layers.38. | FLOAT | 1.000–2 | — |
| layers.39. | FLOAT | 1.000–2 | — |
| layers.40. | FLOAT | 1.000–2 | — |
| layers.41. | FLOAT | 1.000–2 | — |
| video_head_modulation. | FLOAT | 1.000–2 | — |
| final_norm_video. | FLOAT | 1.000–2 | — |
| final_linear_video. | FLOAT | 1.000–2 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |