{
  "revision": 0,
  "last_node_id": 37,
  "last_link_id": 0,
  "nodes": [
    {
      "id": 37,
      "type": "0a99445a-aaf8-4a7f-aec3-d7d710ae1495",
      "pos": [
        2160,
        -360
      ],
      "size": [
        260,
        154
      ],
      "flags": {},
      "order": 4,
      "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": [
          [
            "34",
            "value"
          ],
          [
            "35",
            "value"
          ],
          [
            "33",
            "value"
          ],
          [
            "31",
            "choice"
          ],
          [
            "32",
            "color"
          ]
        ]
      },
      "widgets_values": [],
      "title": "Glow"
    }
  ],
  "links": [],
  "version": 0.4,
  "definitions": {
    "subgraphs": [
      {
        "id": "0a99445a-aaf8-4a7f-aec3-d7d710ae1495",
        "version": 1,
        "state": {
          "lastGroupId": 0,
          "lastNodeId": 36,
          "lastLinkId": 53,
          "lastRerouteId": 0
        },
        "revision": 0,
        "config": {},
        "name": "Glow",
        "inputNode": {
          "id": -10,
          "bounding": [
            2110,
            -165,
            120,
            60
          ]
        },
        "outputNode": {
          "id": -20,
          "bounding": [
            3170,
            -165,
            120,
            60
          ]
        },
        "inputs": [
          {
            "id": "ffc7cf94-be90-4d56-a3b8-d0514d61c015",
            "name": "images.image0",
            "type": "IMAGE",
            "linkIds": [
              45
            ],
            "localized_name": "images.image0",
            "label": "image",
            "pos": [
              2210,
              -145
            ]
          }
        ],
        "outputs": [
          {
            "id": "04986101-50be-4762-8957-8e2a5e460bbb",
            "name": "IMAGE0",
            "type": "IMAGE",
            "linkIds": [
              53
            ],
            "localized_name": "IMAGE0",
            "label": "IMAGE",
            "pos": [
              3190,
              -145
            ]
          }
        ],
        "widgets": [],
        "nodes": [
          {
            "id": 30,
            "type": "GLSLShader",
            "pos": [
              2590,
              -520
            ],
            "size": [
              520,
              272
            ],
            "flags": {},
            "order": 5,
            "mode": 0,
            "inputs": [
              {
                "label": "image0",
                "localized_name": "images.image0",
                "name": "images.image0",
                "type": "IMAGE",
                "link": 45
              },
              {
                "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": 51
              },
              {
                "label": "u_float1",
                "localized_name": "floats.u_float1",
                "name": "floats.u_float1",
                "shape": 7,
                "type": "FLOAT",
                "link": 50
              },
              {
                "label": "u_float2",
                "localized_name": "floats.u_float2",
                "name": "floats.u_float2",
                "shape": 7,
                "type": "FLOAT",
                "link": 52
              },
              {
                "label": "u_float3",
                "localized_name": "floats.u_float3",
                "name": "floats.u_float3",
                "shape": 7,
                "type": "FLOAT",
                "link": null
              },
              {
                "label": "u_int0",
                "localized_name": "ints.u_int0",
                "name": "ints.u_int0",
                "shape": 7,
                "type": "INT",
                "link": 46
              },
              {
                "label": "u_int1",
                "localized_name": "ints.u_int1",
                "name": "ints.u_int1",
                "shape": 7,
                "type": "INT",
                "link": 47
              },
              {
                "label": "u_int2",
                "localized_name": "ints.u_int2",
                "name": "ints.u_int2",
                "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": [
                  53
                ]
              },
              {
                "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 mediump float;\n\nuniform sampler2D u_image0;\nuniform int u_int0;      // Blend mode\nuniform int u_int1;      // Color tint\nuniform float u_float0;  // Intensity\nuniform float u_float1;  // Radius\nuniform float u_float2;  // Threshold\n\nin vec2 v_texCoord;\nout vec4 fragColor;\n\nconst int BLEND_ADD      = 0;\nconst int BLEND_SCREEN   = 1;\nconst int BLEND_SOFT     = 2;\nconst int BLEND_OVERLAY  = 3;\nconst int BLEND_LIGHTEN  = 4;\n\nconst float GOLDEN_ANGLE = 2.39996323;\nconst int MAX_SAMPLES = 48;\nconst vec3 LUMA = vec3(0.299, 0.587, 0.114);\n\nfloat hash(vec2 p) {\n    p = fract(p * vec2(123.34, 456.21));\n    p += dot(p, p + 45.32);\n    return fract(p.x * p.y);\n}\n\nvec3 hexToRgb(int h) {\n    return vec3(\n        float((h >> 16) & 255),\n        float((h >> 8) & 255),\n        float(h & 255)\n    ) * (1.0 / 255.0);\n}\n\nvec3 blend(vec3 base, vec3 glow, int mode) {\n    if (mode == BLEND_SCREEN) {\n        return 1.0 - (1.0 - base) * (1.0 - glow);\n    }\n    if (mode == BLEND_SOFT) {\n        return mix(\n            base - (1.0 - 2.0 * glow) * base * (1.0 - base),\n            base + (2.0 * glow - 1.0) * (sqrt(base) - base),\n            step(0.5, glow)\n        );\n    }\n    if (mode == BLEND_OVERLAY) {\n        return mix(\n            2.0 * base * glow,\n            1.0 - 2.0 * (1.0 - base) * (1.0 - glow),\n            step(0.5, base)\n        );\n    }\n    if (mode == BLEND_LIGHTEN) {\n        return max(base, glow);\n    }\n    return base + glow;\n}\n\nvoid main() {\n    vec4 original = texture(u_image0, v_texCoord);\n    \n    float intensity = u_float0 * 0.05;\n    float radius = u_float1 * u_float1 * 0.012;\n    \n    if (intensity < 0.001 || radius < 0.1) {\n        fragColor = original;\n        return;\n    }\n    \n    float threshold = 1.0 - u_float2 * 0.01;\n    float t0 = threshold - 0.15;\n    float t1 = threshold + 0.15;\n    \n    vec2 texelSize = 1.0 / vec2(textureSize(u_image0, 0));\n    float radius2 = radius * radius;\n    \n    float sampleScale = clamp(radius * 0.75, 0.35, 1.0);\n    int samples = int(float(MAX_SAMPLES) * sampleScale);\n    \n    float noise = hash(gl_FragCoord.xy);\n    float angleOffset = noise * GOLDEN_ANGLE;\n    float radiusJitter = 0.85 + noise * 0.3;\n    \n    float ca = cos(GOLDEN_ANGLE);\n    float sa = sin(GOLDEN_ANGLE);\n    vec2 dir = vec2(cos(angleOffset), sin(angleOffset));\n    \n    vec3 glow = vec3(0.0);\n    float totalWeight = 0.0;\n    \n    // Center tap\n    float centerMask = smoothstep(t0, t1, dot(original.rgb, LUMA));\n    glow += original.rgb * centerMask * 2.0;\n    totalWeight += 2.0;\n    \n    for (int i = 1; i < MAX_SAMPLES; i++) {\n        if (i >= samples) break;\n        \n        float fi = float(i);\n        float dist = sqrt(fi / float(samples)) * radius * radiusJitter;\n        \n        vec2 offset = dir * dist * texelSize;\n        vec3 c = texture(u_image0, v_texCoord + offset).rgb;\n        float mask = smoothstep(t0, t1, dot(c, LUMA));\n        \n        float w = 1.0 - (dist * dist) / (radius2 * 1.5);\n        w = max(w, 0.0);\n        w *= w;\n        \n        glow += c * mask * w;\n        totalWeight += w;\n        \n        dir = vec2(\n            dir.x * ca - dir.y * sa,\n            dir.x * sa + dir.y * ca\n        );\n    }\n    \n    glow *= intensity / max(totalWeight, 0.001);\n    \n    if (u_int1 > 0) {\n        glow *= hexToRgb(u_int1);\n    }\n    \n    vec3 result = blend(original.rgb, glow, u_int0);\n    result += (noise - 0.5) * (1.0 / 255.0);\n    \n    fragColor = vec4(clamp(result, 0.0, 1.0), original.a);\n}",
              "from_input"
            ]
          },
          {
            "id": 34,
            "type": "PrimitiveFloat",
            "pos": [
              2290,
              -510
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 0,
            "mode": 0,
            "inputs": [
              {
                "label": "intensity",
                "localized_name": "value",
                "name": "value",
                "type": "FLOAT",
                "widget": {
                  "name": "value"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "FLOAT",
                "name": "FLOAT",
                "type": "FLOAT",
                "links": [
                  51
                ]
              }
            ],
            "properties": {
              "Node name for S&R": "PrimitiveFloat",
              "min": 0,
              "max": 100,
              "precision": 1,
              "step": 1
            },
            "widgets_values": [
              0
            ]
          },
          {
            "id": 35,
            "type": "PrimitiveFloat",
            "pos": [
              2290,
              -410
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 1,
            "mode": 0,
            "inputs": [
              {
                "label": "radius",
                "localized_name": "value",
                "name": "value",
                "type": "FLOAT",
                "widget": {
                  "name": "value"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "FLOAT",
                "name": "FLOAT",
                "type": "FLOAT",
                "links": [
                  50
                ]
              }
            ],
            "properties": {
              "Node name for S&R": "PrimitiveFloat",
              "min": 0,
              "max": 100,
              "precision": 1,
              "step": 1
            },
            "widgets_values": [
              25
            ]
          },
          {
            "id": 33,
            "type": "PrimitiveFloat",
            "pos": [
              2290,
              -310
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 2,
            "mode": 0,
            "inputs": [
              {
                "label": "threshold",
                "localized_name": "value",
                "name": "value",
                "type": "FLOAT",
                "widget": {
                  "name": "value"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "FLOAT",
                "name": "FLOAT",
                "type": "FLOAT",
                "links": [
                  52
                ]
              }
            ],
            "properties": {
              "Node name for S&R": "PrimitiveFloat",
              "min": 0,
              "max": 100,
              "precision": 1,
              "step": 1
            },
            "widgets_values": [
              100
            ]
          },
          {
            "id": 32,
            "type": "ColorToRGBInt",
            "pos": [
              2290,
              -210
            ],
            "size": [
              270,
              58
            ],
            "flags": {},
            "order": 3,
            "mode": 0,
            "inputs": [
              {
                "label": "color_tint",
                "localized_name": "color",
                "name": "color",
                "type": "COLOR",
                "widget": {
                  "name": "color"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "rgb_int",
                "name": "rgb_int",
                "type": "INT",
                "links": [
                  47
                ]
              }
            ],
            "properties": {
              "Node name for S&R": "ColorToRGBInt"
            },
            "widgets_values": [
              "#45edf5"
            ]
          },
          {
            "id": 31,
            "type": "CustomCombo",
            "pos": [
              2290,
              -110
            ],
            "size": [
              270,
              222
            ],
            "flags": {},
            "order": 4,
            "mode": 0,
            "inputs": [
              {
                "label": "blend_mode",
                "localized_name": "choice",
                "name": "choice",
                "type": "COMBO",
                "widget": {
                  "name": "choice"
                },
                "link": null
              }
            ],
            "outputs": [
              {
                "localized_name": "STRING",
                "name": "STRING",
                "type": "STRING",
                "links": null
              },
              {
                "localized_name": "INDEX",
                "name": "INDEX",
                "type": "INT",
                "links": [
                  46
                ]
              }
            ],
            "properties": {
              "Node name for S&R": "CustomCombo"
            },
            "widgets_values": [
              "add",
              0,
              "add",
              "screen",
              "soft",
              "overlay",
              "lighten",
              ""
            ]
          }
        ],
        "groups": [],
        "links": [
          {
            "id": 51,
            "origin_id": 34,
            "origin_slot": 0,
            "target_id": 30,
            "target_slot": 2,
            "type": "FLOAT"
          },
          {
            "id": 50,
            "origin_id": 35,
            "origin_slot": 0,
            "target_id": 30,
            "target_slot": 3,
            "type": "FLOAT"
          },
          {
            "id": 52,
            "origin_id": 33,
            "origin_slot": 0,
            "target_id": 30,
            "target_slot": 4,
            "type": "FLOAT"
          },
          {
            "id": 46,
            "origin_id": 31,
            "origin_slot": 1,
            "target_id": 30,
            "target_slot": 6,
            "type": "INT"
          },
          {
            "id": 47,
            "origin_id": 32,
            "origin_slot": 0,
            "target_id": 30,
            "target_slot": 7,
            "type": "INT"
          },
          {
            "id": 45,
            "origin_id": -10,
            "origin_slot": 0,
            "target_id": 30,
            "target_slot": 0,
            "type": "IMAGE"
          },
          {
            "id": 53,
            "origin_id": 30,
            "origin_slot": 0,
            "target_id": -20,
            "target_slot": 0,
            "type": "IMAGE"
          }
        ],
        "extra": {
          "workflowRendererVersion": "LG"
        },
        "category": "Image Tools/Color adjust",
        "description": "Adds a glow/bloom effect around bright image areas via GPU fragment shader."
      }
    ]
  }
}