- Use random noise images instead of solid-color (blur had no effect on spatially uniform images, making calibration a no-op) - Generate images per-batch to avoid 200 MB upfront allocation - Simplify hook algebraically: patches + delta * pc1_dir eliminates projection/reconstruction/residual intermediates (~24 MB per step) - Move SCALE_FACTOR, SHIFT_FACTOR, find_step_index to core/sampling.py - Rename sd → shd to avoid confusion with state_dict convention - Remove redundant [:,:,:,:3] slice and unused imports
ComfyUI-LCS
Training-free color control via the Latent Color Subspace.
Note: This is an unofficial community implementation. For the official code, see ExplainableML/LCS.
Based on "The Latent Color Subspace" (ICML 2026): color in diffusion model latent patch spaces lives in a 3D subspace (PCA captures 100% color variance), while the remaining 61 dimensions encode structure and detail orthogonally.
This plugin steers colors directly in the 3D LCS during diffusion sampling — no training, no LoRA, no post-processing.
LCS vs Traditional Post-Processing
LCS operates during diffusion sampling, not after — this is the key difference from traditional color grading (Photoshop, filters, etc.).
| Traditional Post-Processing | LCS | |
|---|---|---|
| When | After VAE decode, in pixel space | During sampling, in latent space |
| Mechanism | Color filter on the final image | Modifies 3D color subspace mid-generation |
| Model awareness | None — structure already locked | Model adapts to color shifts in subsequent steps |
| Result | Colors can look "painted on" | Colors look naturally intended by the model |
For example: to get a warm orange sunset, post-processing tints everything orange (muddying shadows and skin tones), while LCS nudges the color subspace early in sampling so clouds, lighting, and reflections are coherently warm.
The core insight: color and structure are orthogonal in the latent patch space — you can steer one without disturbing the other.
Tested Models
| Model | Status |
|---|---|
| FLUX | Tested |
| z-image | Tested |
| z-image-turbo | Tested |
LCS calibrates per-VAE, so it should work with any model using a compatible VAE. Feel free to report results with other models.
Features
- Color Steering — Push colors toward any target color
- Batch Multi-Color — Different colors per batch item
- Tone Adjustment — Contrast, brightness, saturation, temperature with one-click presets
- Localized Control — Optional mask for region-specific changes
- Latent Color Preview — Visualize color structure without VAE decoding
- Step Observer — Per-step color previews for debugging
Installation
cd ComfyUI/custom_nodes
git clone https://github.com/facok/ComfyUI-LCS.git
Dependencies (usually already present in ComfyUI):
pip install einops safetensors
Quick Start
Basic Color Control
LCS Load Data → LCS Color Intervene → KSampler
↑
(pick a color)
- LCS Load Data — connect your VAE (auto-calibrates on first run)
- LCS Color Intervene — connect MODEL and LCS_DATA, pick a target color
- Connect the output MODEL to KSampler
Tone Adjustment
LCS Load Data → LCS Tone Adjust → KSampler
- LCS Load Data → LCS Tone Adjust
- Select a preset (e.g., "Cinematic") or adjust sliders manually
Multi-Color Batch
LCS Load Data → LCS Color Batch → KSampler
↓
batch_size → EmptyLatentImage
Enter comma-separated hex colors (e.g., #FF0000,#00FF00,#0000FF). Each color applies to one batch item.
Nodes
Calibration
| Node | Description |
|---|---|
| LCS Load Data | Auto-calibrate and cache LCS data per-VAE. Fingerprints VAE weights for automatic cache management — just connect your VAE. |
Calibration runs once per VAE and caches automatically. Subsequent runs load instantly.
Intervention
| Node | Description |
|---|---|
| LCS Color Intervene | Steer colors toward a target. Supports Type I (LCS shift), Type II (HSL shift), or interpolated mode. |
| LCS Color Batch | Different target colors per batch item. Outputs batch_size for EmptyLatentImage. |
| LCS Tone Adjust | Contrast, brightness, saturation, temperature. Preset dropdown with real-time slider sync. |
Observation
| Node | Description |
|---|---|
| LCS Preview Colors | Decode latent colors to RGB preview without VAE decoding. |
| LCS Step Observer | Save per-step color preview PNGs to ComfyUI temp directory. |
Intervention Modes
| Mode | Description | Best For |
|---|---|---|
| interpolated (default) | Blends Type I and Type II using sigma | General use |
| type_i | Direct translation in 3D LCS space | Strong global color shifts |
| type_ii | Per-patch HSL interpolation via bicone geometry | Precise local color control |
Key Parameters
- strength (0.0–2.0): Intervention intensity. 1.0 = full, 0.0 = none.
- start_step / end_step: Step range for intervention. Paper optimal: steps 8–10 of 50.
- mask: Optional. Downsampled to patch grid for localized control.
Tone Presets
Select a preset — sliders update in real-time. Tweak after selecting for fine-tuning. Select Custom to set values manually.
| Preset | Contrast | Brightness | Saturation | Temperature |
|---|---|---|---|---|
| Base | 1.0 | 0.0 | 1.0 | 0.0 |
| Cinematic | 1.20 | -0.05 | 0.90 | 0.05 |
| HDR | 1.40 | 0.0 | 1.20 | 0.0 |
| Vivid | 1.10 | 0.0 | 1.50 | 0.0 |
| Dramatic | 1.50 | -0.10 | 0.85 | 0.0 |
| Low Key | 1.30 | -0.20 | 0.80 | 0.0 |
| High Key | 0.80 | 0.20 | 0.90 | 0.0 |
| Warm | 1.05 | 0.03 | 1.10 | 0.30 |
| Cool | 1.05 | 0.0 | 1.05 | -0.30 |
| Desaturated | 1.0 | 0.0 | 0.40 | 0.0 |
How It Works
- Project — Convert denoised prediction to 64D patch space, project onto 3D LCS basis
- Decompose — Separate 3D color coordinates from the 61D structural residual
- Normalize — Transform to reference timestep (t=50) using learned alpha/beta statistics
- Manipulate — Shift colors, adjust tone, or apply other transformations in 3D LCS
- Reconstruct — Denormalize, add back the preserved 61D residual, convert to latent space
The 61D residual (structure, texture, detail) is never modified — only the 3D color subspace is touched.
File Structure
ComfyUI-LCS/
├── __init__.py # Entry point (V3 + V2 compat)
├── requirements.txt
├── core/
│ ├── calibration.py # PCA calibration pipeline
│ ├── color_space.py # Bicone LCS ↔ HSL mapping
│ ├── defaults.py # Alpha/beta tables from paper
│ ├── lcs_data.py # LCSData dataclass
│ ├── patchify.py # Patch ↔ latent conversion
│ └── timestep.py # Sigma/timestep utilities
├── nodes/
│ ├── calibrate.py # LCSLoadData (auto-calibrate + cache)
│ ├── intervene.py # LCSColorIntervene, LCSColorBatch, LCSToneAdjust
│ └── observe.py # LCSPreviewColors, LCSStepObserver
├── data/ # Cached calibration files
└── web/js/
└── tone_preset.js # Frontend preset sync
Citation
Official repository: ExplainableML/LCS
@article{pach2026latentcolorsubspace,
title={The Latent Color Subspace: Emergent Order in High-Dimensional Chaos},
author={Mateusz Pach and Jessica Bader and Quentin Bouniot and Serge Belongie and Zeynep Akata},
journal={arxiv},
year={2026}
}
Acknowledgments
Thanks to Mateusz Pach, Jessica Bader, Quentin Bouniot, Serge Belongie, and Zeynep Akata for their research making training-free color control possible.
License
MIT