{
  "license": "CC0-1.0",
  "shader": {
    "id": "14iy1g86",
    "title": "Wagara Kamon",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "a very sharp katana",
    "status": "active",
    "compile_ok": true,
    "render_hash": "1f62ae5ce07c9f11",
    "tags": [
      "vj",
      "japan",
      "generator",
      "geometry",
      "pattern"
    ],
    "parents": [],
    "author": "84sx248959ax",
    "author_name": "akx",
    "author_url": "/u/akx",
    "created_at": "2026-08-13T14:53:10.430Z",
    "poster_url": "/posters/14iy1g86.avif",
    "stats": {
      "slops": 1,
      "views": 172,
      "forks": 0
    },
    "self": "/api/v1/shaders/14iy1g86",
    "isf": "/*{\n    \"DESCRIPTION\": \"Wagara Kamon - a Japanese textile-pattern generator built for VJing. One 'morph' knob walks four traditional wagara: seigaiha (blue ocean waves) -> asanoha (hemp leaf) -> shippou (seven treasures) -> yagasuri (arrow fletching), all drawn as their real construction rather than faked with noise: seigaiha uses the unit-circle lattice where neighbours cut every fan into an exact 120-degree scallop, and asanoha is the triangular lattice with a Y from every triangle's centroid, which resolves to twelve radial spokes per hexagonal cell. 'crest' stamps a kamon over the field: a spinning 16-petal kikumon whose disc inverts the pattern's ink/paper polarity like a stencilled family emblem. 'fill' travels from thin line-art to solid woodblock bands; 'weight' is resolution-aware so lines never alias out at high density. Single pass, no feedback buffer, no internal state: every parameter is read straight, so a host that automates natively owns all the motion. 'scroll' and 'twist' are phase positions that wrap at 0/1 onto an exact lattice period, so any ramp or LFO driving them runs forever without a seam; 'flow' and 'spin' are free-run rates for when you would rather not automate at all. 'hit' takes a beat envelope directly: it kicks the zoom, swells the crest, and throws an accent shockwave that expands as the envelope decays. 'energy' maps straight to audio RMS. Four traditional palettes (indigo/vermilion/sumi/gold) blend to a hue-driven neon via 'neon', and 'inkOnly' drops the paper for black-ground additive layering.\",\n    \"CREDIT\": \"CC0\",\n    \"CATEGORIES\": [\"generator\", \"vj\", \"pattern\", \"geometry\"],\n    \"INPUTS\": [\n        { \"NAME\": \"morph\",     \"TYPE\": \"float\", \"DEFAULT\": 0.0,  \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"density\",   \"TYPE\": \"float\", \"DEFAULT\": 0.45, \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"weight\",    \"TYPE\": \"float\", \"DEFAULT\": 0.45, \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"fill\",      \"TYPE\": \"float\", \"DEFAULT\": 0.35, \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"scroll\",    \"TYPE\": \"float\", \"DEFAULT\": 0.0,  \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"flow\",      \"TYPE\": \"float\", \"DEFAULT\": 0.30, \"MIN\": -1.0, \"MAX\": 1.0 },\n        { \"NAME\": \"twist\",     \"TYPE\": \"float\", \"DEFAULT\": 0.0,  \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"spin\",      \"TYPE\": \"float\", \"DEFAULT\": 0.06, \"MIN\": -1.0, \"MAX\": 1.0 },\n        { \"NAME\": \"crest\",     \"TYPE\": \"float\", \"DEFAULT\": 0.85, \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"crestSize\", \"TYPE\": \"float\", \"DEFAULT\": 0.42, \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"palette\",   \"TYPE\": \"long\",  \"DEFAULT\": 0,\n          \"VALUES\": [0, 1, 2, 3],\n          \"LABELS\": [\"Ai (indigo)\", \"Shu (vermilion)\", \"Sumi (ink)\", \"Kin (gold)\"] },\n        { \"NAME\": \"hue\",       \"TYPE\": \"float\", \"DEFAULT\": 0.33, \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"neon\",      \"TYPE\": \"float\", \"DEFAULT\": 0.0,  \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"energy\",    \"TYPE\": \"float\", \"DEFAULT\": 0.25, \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"hit\",       \"TYPE\": \"float\", \"DEFAULT\": 0.0,  \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"glow\",      \"TYPE\": \"float\", \"DEFAULT\": 0.3,  \"MIN\":  0.0, \"MAX\": 1.0 },\n        { \"NAME\": \"inkOnly\",   \"TYPE\": \"bool\",  \"DEFAULT\": false }\n    ]\n}*/\n\n#define TAU 6.28318530718\n#define PI  3.14159265359\n#define SQ3 1.73205080757\n\nvec3 hueRGB(float h) {\n    return 0.5 + 0.5 * cos(TAU * (h + vec3(0.0, 0.333333, 0.666667)));\n}\n\nmat2 rot2(float a) {\n    float c = cos(a), s = sin(a);\n    return mat2(c, -s, s, c);\n}\n\n// anti-aliased stroke coverage; px is the pattern-space size of one screen pixel\nfloat stroke(float d, float w, float px) {\n    return smoothstep(w + px, w - px, abs(d));\n}\n\n// alternating 0/1 bands, edges landing exactly on integer x\nfloat bands(float x, float e) {\n    return smoothstep(-e, e, -sin(PI * x));\n}\n\n// ---------------------------------------------------------------------------\n// seigaiha - overlapping fans of concentric arcs.\n// Unit circles on rows 0.5 apart, x-spacing sqrt(3), odd rows offset half a\n// step: the exact lattice where each fan's neighbours cut it into a 120-degree\n// scallop with no same-row overlap. Front-most (lowest) circle wins, so every\n// visible fan opens upward from its own centre.\n// returns (line, glow, band)\nvec3 seigaiha(vec2 p, float px, float w) {\n    vec2  q  = p * SQ3;\n    float pq = px * SQ3;\n    float rings = 4.0;\n\n    float j0 = floor(q.y * 2.0 + 0.5);\n    float bestY = 1000.0;\n    vec2  bestC = vec2(0.0);\n\n    for (int jj = 0; jj < 5; jj++) {\n        float j  = j0 + float(jj) - 2.0;\n        float cy = j * 0.5;\n        float ox = mod(j, 2.0) * (SQ3 * 0.5);\n        float i0 = floor((q.x - ox) / SQ3 + 0.5);\n        for (int ii = 0; ii < 3; ii++) {\n            vec2 c = vec2((i0 + float(ii) - 1.0) * SQ3 + ox, cy);\n            if (length(q - c) < 1.0 && cy < bestY) { bestY = cy; bestC = c; }\n        }\n    }\n\n    float d  = length(q - bestC);\n    float bd = 1.0 - d;                       // this fan's own outer arc\n    for (int jj = 0; jj < 5; jj++) {\n        float j  = j0 + float(jj) - 2.0;\n        float cy = j * 0.5;\n        if (cy < bestY - 0.01) {              // circles in front cut the fan\n            float ox = mod(j, 2.0) * (SQ3 * 0.5);\n            float i0 = floor((q.x - ox) / SQ3 + 0.5);\n            for (int ii = 0; ii < 3; ii++) {\n                vec2 c = vec2((i0 + float(ii) - 1.0) * SQ3 + ox, cy);\n                bd = min(bd, length(q - c) - 1.0);\n            }\n        }\n    }\n\n    float rd = abs(fract(d * rings + 0.5) - 0.5) / rings;   // nearest arc\n    float ld = min(rd, bd);\n    float e  = min(PI * rings * pq * 1.5, 1.0);\n    // ld lives in q-space, so the widths travel there too\n    return vec3(stroke(ld, w * SQ3, pq), stroke(ld, w * SQ3 * 3.0, pq * 2.5),\n                bands(d * rings, e));\n}\n\n// ---------------------------------------------------------------------------\n// asanoha - hemp leaf. A triangular lattice of dots where every triangle also\n// carries a Y from its centroid out to its three corners. Read from one dot's\n// hexagonal Voronoi cell that is exactly twelve radial spokes: six lattice\n// edges leaving through the edge midpoints, six Y-arms out to the corners.\n// The honeycomb itself is NOT part of the pattern - it only fades in as a\n// containing outline once 'fill' starts blocking in the leaves.\nvec3 asanoha(vec2 p, float px, float w) {\n    vec2 s = vec2(1.0, SQ3);\n    vec2 a = mod(p, s) - s * 0.5;\n    vec2 b = mod(p - s * 0.5, s) - s * 0.5;\n    vec2 g = (dot(a, a) < dot(b, b)) ? a : b;\n\n    float th = atan(g.y, g.x);\n    float f  = mod(th + PI / 12.0, PI / 6.0) - PI / 12.0;   // spokes every 30 deg\n    float sd = length(g) * abs(sin(f));\n\n    vec2  n2 = vec2( 0.5, 0.8660254);\n    vec2  n3 = vec2(-0.5, 0.8660254);\n    float hx = max(abs(g.x), max(abs(dot(g, n2)), abs(dot(g, n3))));\n    float wall = abs(0.5 - hx) + (1.0 - clamp(fill * 3.0, 0.0, 1.0)) * 9.0;\n\n    float ld   = min(sd, wall);\n    float sect = floor(mod(th + PI / 12.0 + TAU, TAU) / (PI / 6.0));\n    return vec3(stroke(ld, w, px), stroke(ld, w * 3.0, px * 2.5), mod(sect, 2.0));\n}\n\n// ---------------------------------------------------------------------------\n// shippou - circles of radius 1/sqrt(2) on a unit grid, so four of them\n// meet at every cell corner. Band = parity of how many discs cover the point.\nvec3 shippou(vec2 p, float px, float w) {\n    float R = 0.70710678;\n    vec2  id = floor(p + 0.5);\n    float dmin = 1000.0;\n    float cnt  = 0.0;\n    for (int j = 0; j < 3; j++) {\n        for (int i = 0; i < 3; i++) {\n            vec2  c  = id + vec2(float(i) - 1.0, float(j) - 1.0);\n            float dd = length(p - c);\n            dmin = min(dmin, abs(dd - R));\n            cnt += (dd < R) ? 1.0 : 0.0;\n        }\n    }\n    return vec3(stroke(dmin, w, px), stroke(dmin, w * 3.0, px * 2.5), mod(cnt, 2.0));\n}\n\n// ---------------------------------------------------------------------------\n// yagasuri - columns of arrow fletching, each column flipped against its\n// neighbours so the chevrons interlock.\nvec3 yagasuri(vec2 p, float px, float w) {\n    float n  = 2.0;                            // stripes per unit\n    float k  = 1.2;                            // chevron slope\n    float id = floor(p.x + 0.5);\n    float lx = p.x - id;\n    float dir = mod(id, 2.0) * 2.0 - 1.0;\n\n    float x  = (p.y * dir + abs(lx) * k) * n;\n    float gm = n * sqrt(1.0 + k * k);          // |grad x| in pattern units\n    float sd = abs(fract(x + 0.5) - 0.5) / gm;\n    float cd = abs(abs(lx) - 0.5);             // column rule\n    float ld = min(sd, cd);\n    float e  = min(PI * gm * px * 1.5, 1.0);\n    return vec3(stroke(ld, w, px), stroke(ld, w * 3.0, px * 2.5), bands(x, e));\n}\n\n// ---------------------------------------------------------------------------\nvoid main() {\n\n    // Phase positions, not accumulators. 'scroll'/'twist' are the automation\n    // targets and wrap at 0/1 onto an exact lattice period, so a host ramp can\n    // run through them forever; 'flow'/'spin' just add free-run drift for when\n    // nothing is automated.\n    float sc   = fract(scroll + TIME * flow * 0.35);\n    float sp   = fract(twist  + TIME * spin * 0.05);\n    float kick = clamp(hit, 0.0, 1.0);          // beat envelope, driven by host\n    float m    = clamp(morph, 0.0, 1.0) * 3.0;  // 0..3 spans the four wagara\n\n    vec2  uv    = (gl_FragCoord.xy - 0.5 * RENDERSIZE.xy) / RENDERSIZE.y;\n    float cells = mix(3.0, 13.0, density) * (1.0 - 0.10 * kick);\n    vec2  p     = rot2(TAU * sp) * uv * cells;\n    float px    = max(cells / RENDERSIZE.y, 0.00001);\n    float w     = max(mix(0.012, 0.055, weight), px * 1.15);\n\n    // Sharpened handover: two patterns only co-exist over the middle 40% of\n    // each step, so most of the knob sits on a clean pattern instead of a\n    // double-exposure of two line fields.\n    float w0 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m      )) * 2.5 - 0.75, 0.0, 1.0));\n    float w1 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m - 1.0)) * 2.5 - 0.75, 0.0, 1.0));\n    float w2 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m - 2.0)) * 2.5 - 0.75, 0.0, 1.0));\n    float w3 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m - 3.0)) * 2.5 - 0.75, 0.0, 1.0));\n\n    // Each scroll offset is exactly one lattice period of that pattern, so the\n    // phase wrap is invisible. KA rescales asanoha's cell (12 spokes per cell\n    // reads far denser than a fan or a ring) to match the others by eye.\n    const float KA = 0.60;\n    vec3 fld = vec3(0.0);\n    if (w0 > 0.002) fld += w0 * seigaiha(p      + vec2(0.0, sc * 0.5773503), px,      w);\n    if (w1 > 0.002) fld += w1 * asanoha (p * KA + vec2(0.0, sc * SQ3      ), px * KA, w * KA);\n    if (w2 > 0.002) fld += w2 * shippou (p      + vec2(0.0, sc            ), px,      w);\n    if (w3 > 0.002) fld += w3 * yagasuri(p      + vec2(0.0, sc * 0.5      ), px,      w);\n\n    float cov  = clamp(max(fld.x, fld.z * fill * 0.92), 0.0, 1.0);\n    float halo = fld.y;\n\n    // ---- kamon: petal crown + disc that inverts the field -------------\n    float r  = length(uv);\n    float th = atan(uv.y, uv.x) - TAU * sp * 2.0;\n    float aa = 1.5 / RENDERSIZE.y;\n    float rc = mix(0.10, 0.42, crestSize) * (1.0 + 0.06 * kick);\n\n    float petalR = rc * (1.0 + 0.30 * (0.5 + 0.5 * cos(16.0 * th)));\n    float ringM  = smoothstep(rc * 0.99 + aa, rc * 0.99 - aa, r)\n                 * smoothstep(rc * 0.86 - aa, rc * 0.86 + aa, r);\n    float petalM = smoothstep(petalR + aa, petalR - aa, r)\n                 * smoothstep(rc * 0.99 - aa, rc * 0.99 + aa, r);\n    float seedM  = smoothstep(rc * 0.14 + aa, rc * 0.14 - aa, r);\n    float discM  = smoothstep(rc * 0.87 + aa, rc * 0.87 - aa, r);\n\n    // the emblem reads as a stamp, so it goes opaque early and 'crest' is\n    // really a fade-in rather than a permanent transparency\n    float crestA = smoothstep(0.0, 0.45, crest);\n    cov = mix(cov, 1.0 - cov, discM * crestA);         // stencil polarity flip\n\n    // ---- palette ----------------------------------------------------------\n    vec3 inkC, paperC, accC;\n    if (palette == 0) {                                 // indigo on washi\n        inkC = vec3(0.055, 0.125, 0.330); paperC = vec3(0.930, 0.910, 0.850); accC = vec3(0.860, 0.720, 0.360);\n    } else if (palette == 1) {                          // vermilion\n        inkC = vec3(0.780, 0.150, 0.090); paperC = vec3(0.960, 0.930, 0.860); accC = vec3(0.930, 0.780, 0.300);\n    } else if (palette == 2) {                          // sumi ink\n        inkC = vec3(0.060, 0.060, 0.070); paperC = vec3(0.900, 0.870, 0.800); accC = vec3(0.650, 0.160, 0.120);\n    } else {                                            // gold on lacquer\n        inkC = vec3(0.880, 0.700, 0.280); paperC = vec3(0.045, 0.035, 0.050); accC = vec3(0.960, 0.900, 0.720);\n    }\n\n    vec3 nHue = hueRGB(hue);\n    inkC   = mix(inkC,   nHue,             neon);\n    paperC = mix(paperC, nHue * 0.05,      neon);\n    accC   = mix(accC,   hueRGB(hue + 0.12), neon);\n\n    if (inkOnly) {\n        paperC = vec3(0.0);\n        inkC  /= max(max(inkC.r, max(inkC.g, inkC.b)), 0.001);\n    }\n\n    // ---- compose ----------------------------------------------------------\n    vec3 col = mix(paperC, inkC, cov);\n    col = mix(col, inkC, ringM * crestA);\n    col = mix(col, accC, max(petalM, seedM) * crestA);\n\n    float ground = dot(paperC, vec3(0.299, 0.587, 0.114));\n    float dark   = smoothstep(0.35, 0.05, ground);\n    col = mix(col, inkC, halo * glow * 0.30 * (1.0 - dark));   // ink bleed on paper\n    col += accC * halo * glow * 0.85 * dark;                   // bloom on dark\n\n    // beat shockwave\n    float rad   = (1.0 - kick) * 1.15;\n    float dsh   = (r - rad) * 9.0;\n    float shock = exp(-dsh * dsh) * kick;\n    col = mix(col, accC, clamp(shock, 0.0, 1.0) * 0.85);\n\n    // Energy pushes contrast rather than raw brightness: a straight gain just\n    // blows a near-white washi ground out to flat paper.\n    float punch = clamp(energy + kick, 0.0, 1.0);\n    col = mix(col, clamp((col - 0.5) * (1.0 + 0.65 * punch) + 0.5, 0.0, 1.0), 0.85);\n    col += accC * punch * 0.25 * dark;\n    col *= 1.0 - 0.28 * dot(uv, uv);\n\n    col = col / (1.0 + max(col - 1.0, 0.0));\n    col = clamp(col, 0.0, 1.0);\n\n    float alpha = inkOnly ? clamp(max(cov, halo * glow), 0.0, 1.0) : 1.0;\n    gl_FragColor = vec4(col, alpha);\n}\n",
    "generator": {
      "model": "claude-opus-5",
      "provider": null,
      "prompt": null,
      "params": null
    }
  }
}