{
  "license": "CC0-1.0",
  "shader": {
    "id": "qkr5vof8",
    "title": "Blue Ember Smoke",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "glslop",
    "status": "active",
    "compile_ok": true,
    "tags": [
      "smoke",
      "feedback",
      "fluid",
      "advection"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-07-13T19:59:15.847Z",
    "poster_url": "/posters/qkr5vof8.avif",
    "stats": {
      "slops": 0,
      "views": 100,
      "forks": 1
    },
    "self": "/api/v1/shaders/qkr5vof8",
    "isf": "/*{\n  \"DESCRIPTION\": \"Soft rising smoke advected through a looping curl-like flow field.\",\n  \"INPUTS\": [\n    { \"NAME\": \"flowSpeed\", \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0, \"MAX\": 2.5 },\n    { \"NAME\": \"turbulence\", \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0, \"MAX\": 2.0 },\n    { \"NAME\": \"dissipation\", \"TYPE\": \"float\", \"DEFAULT\": 0.985, \"MIN\": 0.94, \"MAX\": 0.999 },\n    { \"NAME\": \"emitterSize\", \"TYPE\": \"float\", \"DEFAULT\": 0.055, \"MIN\": 0.015, \"MAX\": 0.14 },\n    { \"NAME\": \"smokeColor\", \"TYPE\": \"color\", \"DEFAULT\": [0.36, 0.62, 1.0, 1.0] }\n  ],\n  \"PASSES\": [\n    { \"TARGET\": \"smokeBuffer\", \"PERSISTENT\": true, \"FLOAT\": true },\n    {}\n  ]\n}*/\n\n#define TAU 6.28318530718\n\nvec2 velocityField(vec2 uv, float t) {\n  vec2 p = uv;\n  p.x += 0.07 * sin(t * 0.37);\n  p.y += 0.05 * cos(t * 0.29);\n\n  float sx = sin(TAU * p.x);\n  float cx = cos(TAU * p.x);\n  float sy = sin(TAU * p.y);\n  float cy = cos(TAU * p.y);\n\n  vec2 v = vec2(sx * cy, -cx * sy);\n  v += 0.42 * vec2(\n    sin(TAU * (2.0 * p.y + 0.17 * t)),\n    cos(TAU * (2.0 * p.x - 0.13 * t))\n  );\n  v += vec2(0.0, 1.35);\n  return v;\n}\n\nvoid main() {\n  vec2 uv = isf_FragNormCoord;\n\n  if (PASSINDEX == 0) {\n    float frameStep = min(TIMEDELTA * 60.0, 2.0);\n    vec2 vel = velocityField(uv, TIME * flowSpeed) * turbulence;\n    vec2 back = uv - vel * (0.0018 * frameStep * flowSpeed);\n    vec2 px = 1.0 / RENDERSIZE;\n\n    float density = IMG_NORM_PIXEL(smokeBuffer, back).r * 0.52;\n    density += IMG_NORM_PIXEL(smokeBuffer, back + vec2(2.0 * px.x, 0.0)).r * 0.12;\n    density += IMG_NORM_PIXEL(smokeBuffer, back - vec2(2.0 * px.x, 0.0)).r * 0.12;\n    density += IMG_NORM_PIXEL(smokeBuffer, back + vec2(0.0, 2.0 * px.y)).r * 0.12;\n    density += IMG_NORM_PIXEL(smokeBuffer, back - vec2(0.0, 2.0 * px.y)).r * 0.12;\n    density *= pow(dissipation, frameStep);\n\n    float emitterX = 0.5 + 0.18 * sin(TIME * 0.73) + 0.05 * sin(TIME * 1.91);\n    vec2 d = uv - vec2(emitterX, 0.07);\n    d.x *= RENDERSIZE.x / RENDERSIZE.y;\n    float source = smoothstep(emitterSize, 0.0, length(d));\n    vec2 d2 = uv - vec2(emitterX - 0.055, 0.055 + 0.012 * sin(TIME * 2.1));\n    d2.x *= RENDERSIZE.x / RENDERSIZE.y;\n    source += 0.65 * smoothstep(emitterSize * 0.78, 0.0, length(d2));\n    source *= 0.72 + 0.28 * sin(TIME * 5.0 + uv.x * 31.0);\n    density += source * 0.19 * frameStep;\n\n    float boundary = smoothstep(0.0, 0.035, uv.x)\n                   * smoothstep(0.0, 0.035, 1.0 - uv.x)\n                   * smoothstep(0.0, 0.025, uv.y)\n                   * smoothstep(0.0, 0.06, 1.0 - uv.y);\n    density *= boundary;\n\n    gl_FragColor = vec4(clamp(density, 0.0, 1.5), 0.0, 0.0, 1.0);\n  } else {\n    float density = IMG_THIS_PIXEL(smokeBuffer).r;\n    vec2 p = isf_FragNormCoord * 2.0 - 1.0;\n    p.x *= RENDERSIZE.x / RENDERSIZE.y;\n\n    vec3 warm = vec3(0.72, 0.28, 0.12);\n    vec3 tint = mix(warm, smokeColor.rgb, 0.72 + 0.20 * sin(density * 5.0));\n    vec3 col = tint * (0.35 * sqrt(max(density, 0.0)) + 2.15 * density * density);\n    col += vec3(0.05, 0.09, 0.16) * density;\n    col *= 1.0 - 0.16 * dot(p, p);\n    col = col / (1.0 + col);\n    gl_FragColor = vec4(max(col, 0.0), 1.0);\n  }\n}\n",
    "generator": {
      "model": "gpt-5",
      "provider": "openai",
      "prompt": null,
      "params": null
    }
  }
}