{
  "revision": 0,
  "last_node_id": 14,
  "last_link_id": 0,
  "nodes": [
    {
      "id": 14,
      "type": "36677b92-5dd8-47a5-9380-4da982c1894f",
      "pos": [
        3610,
        -2630
      ],
      "size": [
        270,
        150
      ],
      "flags": {},
      "order": 3,
      "mode": 0,
      "inputs": [
        {
          "label": "image",
          "localized_name": "images.image0",
          "name": "images.image0",
          "type": "IMAGE",
          "link": null
        }
      ],
      "outputs": [
        {
          "label": "IMAGE",
          "localized_name": "IMAGE0",
          "name": "IMAGE0",
          "type": "IMAGE",
          "links": []
        }
      ],
      "properties": {
        "proxyWidgets": [
          [
            "4",
            "value"
          ],
          [
            "5",
            "value"
          ],
          [
            "7",
            "value"
          ],
          [
            "6",
            "value"
          ]
        ]
      },
      "widgets_values": [],
      "title": "Color Adjustment"
    }
  ],
  "links": [],
  "version": 0.4,
  "definitions": {
    "subgraphs": [
      {
        "id": "36677b92-5dd8-47a5-9380-4da982c1894f",
        "version": 1,
        "state": {
          "lastGroupId": 0,
          "lastNodeId": 16,
          "lastLinkId": 36,
          "lastRerouteId": 0
        },
        "revision": 0,
        "config": {},
        "name": "Color Adjustment",
        "inputNode": {
          "id": -10,
          "bounding": [
            3110,
            -3560,
            120,
            60
          ]
        },
        "outputNode": {
          "id": -20,
          "bounding": [
            4070,
            -3560,
            120,
            60
          ]
        },
        "inputs": [
          {
            "id": "0431d493-5f28-4430-bd00-84733997fc08",
            "name": "images.image0",
            "type": "IMAGE",
            "linkIds": [
              29
            ],
            "localized_name": "images.image0",
            "label": "image",
            "pos": [
              3210,
              -3540
            ]
          }
        ],
        "outputs": [
          {
            "id": "bee8ea06-a114-4612-8937-939f2c927bdb",
            "name": "IMAGE0",
            "type": "IMAGE",
            "linkIds": [
              28
            ],
            "localized_name": "IMAGE0",
            "label": "IMAGE",
            "pos": [
              4090,
              -3540
            ]
          }
        ],
        "widgets": [],
        "nodes": [
          {
            "id": 15,
            "type": "GLSLShader",
            "pos": [
              3590,
              -3940
            ],
            "size": [
              420,
              252
            ],
            "flags": {},
            "order": 4,
            "mode": 0,
            "inputs": [
              {
                "label": "image0",
                "localized_name": "images.image0",
                "name": "images.image0",
                "type": "IMAGE",
                "link": 29
              },
              {
                "label": "image1",
                "localized_name": "images.image1",
                "name": "images.image1",
                "shape": 7,
                "type": "IMAGE",
                "link": null
              },
              {
                "label": "u_float0",
                "localized_name": "floats.u_float0",
                "name": "floats.u_float0",
                "shape": 7,
                "type": "FLOAT",
                "link": 34
              },
              {
                "label": "u_float1",
                "localized_name": "floats.u_float1",
                "name": "floats.u_float1",
                "shape": 7,
                "type": "FLOAT",
                "link": 30
              },
              {
                "label": "u_float2",
                "localized_name": "floats.u_float2",
                "name": "floats.u_float2",
                "shape": 7,
                "type": "FLOAT",
                "link": 31
              },
              {
                "label": "u_float3",
                "localized_name": "floats.u_float3",
                "name": "floats.u_float3",
                "shape": 7,
                "type": "FLOAT",
                "link": 33
              },
              {
                "label": "u_float4",
                "localized_name": "floats.u_float4",
                "name": "floats.u_float4",
                "shape": 7,
                "type": "FLOAT",
                "link": null
              },
              {
                "label": "u_int0",
                "localized_name": "ints.u_int0",
                "name": "ints.u_int0",
                "shape": 7,
                "type": "INT",
                "link": null
              },
              {
                "localized_name": "fragment_shader",
                "name": "fragment_shader",
                "type": "STRING",
                "widget": {
                  "name": "fragment_shader"
                },
                "link": null
              },
              {
                "localized_name": "size_mode",
                "name": "size_mode",
                "type": "COMFY_DYNAMICCOMBO_V3",
                "widget": {
                  "name": "size_mode"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "IMAGE0",
                "name": "IMAGE0",
                "type": "IMAGE",
                "links": [
                  28
                ]
              },
              {
                "localized_name": "IMAGE1",
                "name": "IMAGE1",
                "type": "IMAGE",
                "links": null
              },
              {
                "localized_name": "IMAGE2",
                "name": "IMAGE2",
                "type": "IMAGE",
                "links": null
              },
              {
                "localized_name": "IMAGE3",
                "name": "IMAGE3",
                "type": "IMAGE",
                "links": null
              }
            ],
            "properties": {
              "Node name for S&R": "GLSLShader"
            },
            "widgets_values": [
              "#version 300 es\nprecision highp float;\n\nuniform sampler2D u_image0;\nuniform float u_float0; // temperature (-100 to 100)\nuniform float u_float1; // tint (-100 to 100)\nuniform float u_float2; // vibrance (-100 to 100)\nuniform float u_float3; // saturation (-100 to 100)\n\nin vec2 v_texCoord;\nout vec4 fragColor;\n\nconst float INPUT_SCALE = 0.01;\nconst float TEMP_TINT_PRIMARY = 0.3;\nconst float TEMP_TINT_SECONDARY = 0.15;\nconst float VIBRANCE_BOOST = 2.0;\nconst float SATURATION_BOOST = 2.0;\nconst float SKIN_PROTECTION = 0.5;\nconst float EPSILON = 0.001;\nconst vec3 LUMA_WEIGHTS = vec3(0.299, 0.587, 0.114);\n\nvoid main() {\n    vec4 tex = texture(u_image0, v_texCoord);\n    vec3 color = tex.rgb;\n    \n    // Scale inputs: -100/100 \u2192 -1/1\n    float temperature = u_float0 * INPUT_SCALE;\n    float tint = u_float1 * INPUT_SCALE;\n    float vibrance = u_float2 * INPUT_SCALE;\n    float saturation = u_float3 * INPUT_SCALE;\n    \n    // Temperature (warm/cool): positive = warm, negative = cool\n    color.r += temperature * TEMP_TINT_PRIMARY;\n    color.b -= temperature * TEMP_TINT_PRIMARY;\n    \n    // Tint (green/magenta): positive = green, negative = magenta\n    color.g += tint * TEMP_TINT_PRIMARY;\n    color.r -= tint * TEMP_TINT_SECONDARY;\n    color.b -= tint * TEMP_TINT_SECONDARY;\n    \n    // Single clamp after temperature/tint\n    color = clamp(color, 0.0, 1.0);\n    \n    // Vibrance with skin protection\n    if (vibrance != 0.0) {\n        float maxC = max(color.r, max(color.g, color.b));\n        float minC = min(color.r, min(color.g, color.b));\n        float sat = maxC - minC;\n        float gray = dot(color, LUMA_WEIGHTS);\n        \n        if (vibrance < 0.0) {\n            // Desaturate: -100 \u2192 gray\n            color = mix(vec3(gray), color, 1.0 + vibrance);\n        } else {\n            // Boost less saturated colors more\n            float vibranceAmt = vibrance * (1.0 - sat);\n            \n            // Branchless skin tone protection\n            float isWarmTone = step(color.b, color.g) * step(color.g, color.r);\n            float warmth = (color.r - color.b) / max(maxC, EPSILON);\n            float skinTone = isWarmTone * warmth * sat * (1.0 - sat);\n            vibranceAmt *= (1.0 - skinTone * SKIN_PROTECTION);\n            \n            color = mix(vec3(gray), color, 1.0 + vibranceAmt * VIBRANCE_BOOST);\n        }\n    }\n    \n    // Saturation\n    if (saturation != 0.0) {\n        float gray = dot(color, LUMA_WEIGHTS);\n        float satMix = saturation < 0.0\n            ? 1.0 + saturation                      // -100 \u2192 gray\n            : 1.0 + saturation * SATURATION_BOOST;  // +100 \u2192 3x boost\n        color = mix(vec3(gray), color, satMix);\n    }\n    \n    fragColor = vec4(clamp(color, 0.0, 1.0), tex.a);\n}",
              "from_input"
            ]
          },
          {
            "id": 6,
            "type": "PrimitiveFloat",
            "pos": [
              3290,
              -3610
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 0,
            "mode": 0,
            "inputs": [
              {
                "label": "vibrance",
                "localized_name": "value",
                "name": "value",
                "type": "FLOAT",
                "widget": {
                  "name": "value"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "FLOAT",
                "name": "FLOAT",
                "type": "FLOAT",
                "links": [
                  26,
                  31
                ]
              }
            ],
            "title": "Vibrance",
            "properties": {
              "Node name for S&R": "PrimitiveFloat",
              "max": 100,
              "min": -100,
              "step": 1,
              "display": "gradientslider",
              "gradient_stops": [
                {
                  "offset": 0,
                  "color": [
                    128,
                    128,
                    128
                  ]
                },
                {
                  "offset": 1,
                  "color": [
                    255,
                    0,
                    0
                  ]
                }
              ]
            },
            "widgets_values": [
              0
            ]
          },
          {
            "id": 7,
            "type": "PrimitiveFloat",
            "pos": [
              3290,
              -3720
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 1,
            "mode": 0,
            "inputs": [
              {
                "label": "saturation",
                "localized_name": "value",
                "name": "value",
                "type": "FLOAT",
                "widget": {
                  "name": "value"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "FLOAT",
                "name": "FLOAT",
                "type": "FLOAT",
                "links": [
                  33
                ]
              }
            ],
            "title": "Saturation",
            "properties": {
              "Node name for S&R": "PrimitiveFloat",
              "max": 100,
              "min": -100,
              "step": 1,
              "display": "gradientslider",
              "gradient_stops": [
                {
                  "offset": 0,
                  "color": [
                    128,
                    128,
                    128
                  ]
                },
                {
                  "offset": 1,
                  "color": [
                    255,
                    0,
                    0
                  ]
                }
              ]
            },
            "widgets_values": [
              0
            ]
          },
          {
            "id": 5,
            "type": "PrimitiveFloat",
            "pos": [
              3290,
              -3830
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 2,
            "mode": 0,
            "inputs": [
              {
                "label": "tint",
                "localized_name": "value",
                "name": "value",
                "type": "FLOAT",
                "widget": {
                  "name": "value"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "FLOAT",
                "name": "FLOAT",
                "type": "FLOAT",
                "links": [
                  30
                ]
              }
            ],
            "title": "Tint",
            "properties": {
              "Node name for S&R": "PrimitiveFloat",
              "max": 100,
              "min": -100,
              "step": 1,
              "display": "gradientslider",
              "gradient_stops": [
                {
                  "offset": 0,
                  "color": [
                    0,
                    255,
                    0
                  ]
                },
                {
                  "offset": 0.5,
                  "color": [
                    255,
                    255,
                    255
                  ]
                },
                {
                  "offset": 1,
                  "color": [
                    255,
                    0,
                    255
                  ]
                }
              ]
            },
            "widgets_values": [
              0
            ]
          },
          {
            "id": 4,
            "type": "PrimitiveFloat",
            "pos": [
              3290,
              -3940
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 3,
            "mode": 0,
            "inputs": [
              {
                "label": "temperature",
                "localized_name": "value",
                "name": "value",
                "type": "FLOAT",
                "widget": {
                  "name": "value"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "FLOAT",
                "name": "FLOAT",
                "type": "FLOAT",
                "links": [
                  34
                ]
              }
            ],
            "title": "Temperature",
            "properties": {
              "Node name for S&R": "PrimitiveFloat",
              "max": 100,
              "min": -100,
              "step": 1,
              "display": "gradientslider",
              "gradient_stops": [
                {
                  "offset": 0,
                  "color": [
                    68,
                    136,
                    255
                  ]
                },
                {
                  "offset": 0.5,
                  "color": [
                    255,
                    255,
                    255
                  ]
                },
                {
                  "offset": 1,
                  "color": [
                    255,
                    136,
                    0
                  ]
                }
              ]
            },
            "widgets_values": [
              0
            ]
          }
        ],
        "groups": [],
        "links": [
          {
            "id": 34,
            "origin_id": 4,
            "origin_slot": 0,
            "target_id": 15,
            "target_slot": 2,
            "type": "FLOAT"
          },
          {
            "id": 30,
            "origin_id": 5,
            "origin_slot": 0,
            "target_id": 15,
            "target_slot": 3,
            "type": "FLOAT"
          },
          {
            "id": 31,
            "origin_id": 6,
            "origin_slot": 0,
            "target_id": 15,
            "target_slot": 4,
            "type": "FLOAT"
          },
          {
            "id": 33,
            "origin_id": 7,
            "origin_slot": 0,
            "target_id": 15,
            "target_slot": 5,
            "type": "FLOAT"
          },
          {
            "id": 29,
            "origin_id": -10,
            "origin_slot": 0,
            "target_id": 15,
            "target_slot": 0,
            "type": "IMAGE"
          },
          {
            "id": 28,
            "origin_id": 15,
            "origin_slot": 0,
            "target_id": -20,
            "target_slot": 0,
            "type": "IMAGE"
          }
        ],
        "extra": {
          "workflowRendererVersion": "LG"
        },
        "category": "Image Tools/Color adjust",
        "description": "Adjusts saturation, temperature, tint, and vibrance using a real-time GPU fragment shader."
      }
    ]
  }
}
