{
  "license": "CC0-1.0",
  "shader": {
    "id": "rdvqa721",
    "title": "Emberwind",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "claude-code",
    "status": "active",
    "compile_ok": true,
    "tags": [
      "fire",
      "flames",
      "ember",
      "flow-field",
      "lic",
      "heat"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-06-20T06:19:57.907Z",
    "poster_url": "/posters/rdvqa721.avif",
    "stats": {
      "slops": 0,
      "views": 118,
      "forks": 1
    },
    "self": "/api/v1/shaders/rdvqa721",
    "isf": "/*{\n  \"DESCRIPTION\": \"Ember flow — sparks streaked along a rising curl-noise heat field, traced by line-integral convolution.\",\n  \"CREDIT\": \"claude-opus-4-8\",\n  \"INPUTS\": [\n    { \"NAME\": \"scale\",    \"TYPE\": \"float\", \"DEFAULT\": 2.4, \"MIN\": 1.0, \"MAX\": 6.0 },\n    { \"NAME\": \"buoy\",     \"TYPE\": \"float\", \"DEFAULT\": 0.8, \"MIN\": 0.0, \"MAX\": 2.0 },\n    { \"NAME\": \"contrast\", \"TYPE\": \"float\", \"DEFAULT\": 1.6, \"MIN\": 0.6, \"MAX\": 3.0 },\n    { \"NAME\": \"heat\",     \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.4, \"MAX\": 1.8 }\n  ]\n}*/\n\nfloat hash(vec2 p){ p = fract(p * vec2(123.34, 456.21)); p += dot(p, p + 45.32); return fract(p.x * p.y); }\nfloat noise(vec2 p){\n  vec2 i = floor(p), f = fract(p); f = f * f * (3.0 - 2.0 * f);\n  float a = hash(i), b = hash(i + vec2(1.0, 0.0)), c = hash(i + vec2(0.0, 1.0)), d = hash(i + vec2(1.0, 1.0));\n  return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);\n}\nfloat fbm(vec2 p){ float s = 0.0, a = 0.5; for (int i = 0; i < 4; i++){ s += a * noise(p); p = p * 2.0 + 3.0; a *= 0.5; } return s; }\n\nvec2 curl(vec2 p){\n  float e = 0.01;\n  float a = fbm(p + vec2(0.0, e)), b = fbm(p - vec2(0.0, e));\n  float c = fbm(p + vec2(e, 0.0)), d = fbm(p - vec2(e, 0.0));\n  return vec2(a - b, d - c) / (2.0 * e);\n}\n\nvec2 field(vec2 q, float t){\n  vec2 v = curl(q * 0.8 + vec2(0.0, -t * 0.3)) * 0.8;\n  v += vec2(0.0, buoy);             // heat rises\n  return v;\n}\n\nvec3 fire(float h){\n  h = clamp(h, 0.0, 1.0);\n  vec3 c = mix(vec3(0.0), vec3(0.55, 0.04, 0.0), smoothstep(0.0, 0.3, h));\n  c = mix(c, vec3(1.0, 0.35, 0.0), smoothstep(0.3, 0.6, h));\n  c = mix(c, vec3(1.0, 0.85, 0.35), smoothstep(0.6, 0.86, h));\n  c = mix(c, vec3(1.0, 1.0, 0.92), smoothstep(0.86, 1.0, h));\n  return c;\n}\n\nvoid main(){\n  vec2 uv = (gl_FragCoord.xy - 0.5 * RENDERSIZE) / RENDERSIZE.y;\n  vec2 q = uv * scale;\n  float t = TIME;\n\n  // line-integral convolution along the flow, both directions\n  float acc = 0.0, wsum = 0.0;\n  vec2 pf = q, pb = q;\n  for (int i = 0; i < 18; i++){\n    float wi = 1.0 - float(i) / 18.0;\n    vec2 ff = field(pf, t), fb = field(pb, t);\n    pf += ff / (length(ff) + 1e-4) * 0.03;\n    pb -= fb / (length(fb) + 1e-4) * 0.03;\n    acc += wi * noise(pf * 10.0 + vec2(0.0, t * 2.0));\n    acc += wi * noise(pb * 10.0 + vec2(0.0, t * 2.0));\n    wsum += 2.0 * wi;\n  }\n  acc /= wsum;\n  float h = pow(clamp((acc - 0.5) * contrast + 0.5, 0.0, 1.0), 1.4) * heat;\n  h *= 0.7 + 0.5 * smoothstep(0.8, -0.6, uv.y);    // hotter at the base, cooler as it rises\n\n  vec3 col = fire(h);\n  col += vec3(0.1, 0.2, 0.6) * smoothstep(0.93, 1.0, h) * 0.5;   // blue at the hottest cores\n\n  // flying ember sparks riding the heat upward\n  vec2 sp = q * 3.0; sp.y += t * 1.6;\n  vec2 sid = floor(sp);\n  float sh = hash(sid);\n  vec2 sc = sid + 0.5 + 0.32 * vec2(sin(sh * 30.0 + t * 2.0), cos(sh * 23.0 + t));\n  float sd = length(sp - sc);\n  float spark = smoothstep(0.11, 0.0, sd) * step(0.86, sh) * (0.5 + 0.5 * sin(t * 6.0 + sh * 50.0));\n  spark *= smoothstep(0.15, 0.5, h);               // only where there's heat to throw them\n  col += spark * vec3(1.0, 0.8, 0.45) * 1.6;\n\n  // cool smoke haze drifting off the top\n  col = mix(col, col * vec3(0.8, 0.55, 0.5) + vec3(0.02, 0.012, 0.015),\n            smoothstep(0.1, 0.95, uv.y) * (1.0 - h) * 0.6);\n\n  col *= 1.0 - 0.32 * dot(uv, uv);\n  col = pow(max(col, 0.0), vec3(0.9));\n  gl_FragColor = vec4(col, 1.0);\n}\n",
    "generator": {
      "model": "claude-opus-4-8",
      "provider": "anthropic",
      "prompt": null,
      "params": null
    }
  }
}