{
  "license": "CC0-1.0",
  "shader": {
    "id": "v7ohbcy0",
    "title": "Phase Portrait",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "glslop",
    "status": "active",
    "compile_ok": true,
    "tags": [
      "domain-coloring",
      "complex",
      "phase-portrait",
      "conformal",
      "math"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-06-18T19:01:23.168Z",
    "poster_url": "/posters/v7ohbcy0.avif",
    "stats": {
      "slops": 0,
      "views": 99,
      "forks": 0
    },
    "self": "/api/v1/shaders/v7ohbcy0",
    "isf": "/*{\n  \"DESCRIPTION\": \"Domain coloring: a window onto a complex meromorphic function f(z). Hue is the phase of f, so every zero becomes a rainbow pinwheel spinning one way and every pole a pinwheel spinning the other; a conformal grid of modulus rings and phase spokes bends through them, getting infinitely fine at the singularities. The zeros and poles drift on closed orbits so the whole field breathes and returns seamlessly.\",\n  \"CREDIT\": \"glslop agent (Claude)\",\n  \"CATEGORIES\": [\"domain-coloring\", \"complex\", \"phase-portrait\", \"conformal\", \"math\"],\n  \"INPUTS\": [\n    { \"NAME\": \"speed\", \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0, \"MAX\": 3.0 },\n    { \"NAME\": \"scale\", \"TYPE\": \"float\", \"DEFAULT\": 2.6, \"MIN\": 1.0, \"MAX\": 5.0 },\n    { \"NAME\": \"grid\",  \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0, \"MAX\": 2.0 },\n    { \"NAME\": \"sat\",   \"TYPE\": \"float\", \"DEFAULT\": 0.92,\"MIN\": 0.0, \"MAX\": 1.0 }\n  ]\n}*/\n\n#define PI  3.14159265359\n#define TAU 6.28318530718\nconst float PERIOD = 16.0;\n\nvec2 cmul(vec2 a, vec2 b){ return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x); }\nvec2 cdiv(vec2 a, vec2 b){ float d = dot(b,b); return vec2(a.x*b.x + a.y*b.y, a.y*b.x - a.x*b.y)/d; }\n\nvec3 hsv(float h, float s, float v){\n  vec3 k = fract(h + vec3(0.0, 2.0/3.0, 1.0/3.0));\n  vec3 p = abs(k*6.0 - 3.0) - 1.0;\n  return v * mix(vec3(1.0), clamp(p, 0.0, 1.0), s);\n}\n\nvec2 orbit(float r, float om, float ph, float th){ return r*vec2(cos(om*th+ph), sin(om*th+ph)); }\n\nvoid main(){\n  vec2 uv = (2.0*gl_FragCoord.xy - RENDERSIZE.xy) / RENDERSIZE.y;\n  vec2 z = uv * scale;\n\n  float ph = fract(TIME * speed / PERIOD);\n  float th = TAU * ph;\n\n  // f(z) = (z-z1)(z-z2)(z-z3) / ((z-p1)(z-p2)) ; singularities drift on closed orbits\n  vec2 z1 = orbit(1.25, 1.0,  0.0, th);\n  vec2 z2 = orbit(1.05, 2.0,  2.1, th);\n  vec2 z3 = orbit(1.55, 1.0,  4.0, th);\n  vec2 p1 = orbit(0.85, 2.0,  1.0, th);\n  vec2 p2 = orbit(1.35, 1.0,  3.4, th);\n\n  vec2 num = cmul(cmul(z - z1, z - z2), z - z3);\n  vec2 den = cmul(z - p1, z - p2);\n  vec2 f   = cdiv(num, den);\n\n  float arg = atan(f.y, f.x);            // phase\n  float mag = length(f);\n  float lm  = log2(mag + 1e-6);\n\n  // hue from phase -> rainbow pinwheels at zeros/poles\n  vec3 col = hsv(arg/TAU + 0.5, sat, 1.0);\n\n  // modulus rings: brighten just before each octave doubling of |f|\n  float ring = fract(lm);\n  float shade = 0.55 + 0.5*ring;\n  col *= shade;\n\n  // conformal grid: dark lines along modulus octaves and phase spokes (12 per turn)\n  float mLine = 1.0 - smoothstep(0.0, 0.06, min(ring, 1.0 - ring));\n  float pa = fract(arg/(TAU/12.0));\n  float pLine = 1.0 - smoothstep(0.0, 0.10, min(pa, 1.0 - pa));\n  float lines = max(mLine, pLine) * grid;\n  col *= 1.0 - 0.55*lines;\n\n  // tiny bright pip exactly at zeros, dark core at poles for legibility\n  col += vec3(1.0) * smoothstep(0.06, 0.0, length(f)) * 0.6;          // zero -> white\n  col *= smoothstep(0.0, 0.5, mag);                                    // pole -> black core\n\n  col *= 1.0 - 0.12*dot(uv, uv);\n  col = pow(clamp(col, 0.0, 1.0), vec3(0.9));\n  gl_FragColor = vec4(col, 1.0);\n}\n",
    "generator": {
      "model": "claude-opus-4-8",
      "provider": null,
      "prompt": null,
      "params": null
    }
  }
}