fork a shader
Write or paste an ISF shader (GLSL ES with a /*{ ... }*/ header). It compiles + renders in yer browser. New-account submissions go to the moderation queue; trusted level 2+ authors can go live immediately. Everything is CC0. Got a file first? preview it locally →
A model marks this as witnessed_generation (honest AI provenance). Leave blank if you hand-authored it.
/*{ "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.", "CREDIT": "CC0", "CATEGORIES": ["generator", "vj", "pattern", "geometry"], "INPUTS": [ { "NAME": "morph", "TYPE": "float", "DEFAULT": 0.0, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "density", "TYPE": "float", "DEFAULT": 0.45, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "weight", "TYPE": "float", "DEFAULT": 0.45, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "fill", "TYPE": "float", "DEFAULT": 0.35, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "scroll", "TYPE": "float", "DEFAULT": 0.0, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "flow", "TYPE": "float", "DEFAULT": 0.30, "MIN": -1.0, "MAX": 1.0 }, { "NAME": "twist", "TYPE": "float", "DEFAULT": 0.0, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "spin", "TYPE": "float", "DEFAULT": 0.06, "MIN": -1.0, "MAX": 1.0 }, { "NAME": "crest", "TYPE": "float", "DEFAULT": 0.85, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "crestSize", "TYPE": "float", "DEFAULT": 0.42, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "palette", "TYPE": "long", "DEFAULT": 0, "VALUES": [0, 1, 2, 3], "LABELS": ["Ai (indigo)", "Shu (vermilion)", "Sumi (ink)", "Kin (gold)"] }, { "NAME": "hue", "TYPE": "float", "DEFAULT": 0.33, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "neon", "TYPE": "float", "DEFAULT": 0.0, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "energy", "TYPE": "float", "DEFAULT": 0.25, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "hit", "TYPE": "float", "DEFAULT": 0.0, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "glow", "TYPE": "float", "DEFAULT": 0.3, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "inkOnly", "TYPE": "bool", "DEFAULT": false } ] }*/ #define TAU 6.28318530718 #define PI 3.14159265359 #define SQ3 1.73205080757 vec3 hueRGB(float h) { return 0.5 + 0.5 * cos(TAU * (h + vec3(0.0, 0.333333, 0.666667))); } mat2 rot2(float a) { float c = cos(a), s = sin(a); return mat2(c, -s, s, c); } // anti-aliased stroke coverage; px is the pattern-space size of one screen pixel float stroke(float d, float w, float px) { return smoothstep(w + px, w - px, abs(d)); } // alternating 0/1 bands, edges landing exactly on integer x float bands(float x, float e) { return smoothstep(-e, e, -sin(PI * x)); } // --------------------------------------------------------------------------- // seigaiha - overlapping fans of concentric arcs. // Unit circles on rows 0.5 apart, x-spacing sqrt(3), odd rows offset half a // step: the exact lattice where each fan's neighbours cut it into a 120-degree // scallop with no same-row overlap. Front-most (lowest) circle wins, so every // visible fan opens upward from its own centre. // returns (line, glow, band) vec3 seigaiha(vec2 p, float px, float w) { vec2 q = p * SQ3; float pq = px * SQ3; float rings = 4.0; float j0 = floor(q.y * 2.0 + 0.5); float bestY = 1000.0; vec2 bestC = vec2(0.0); for (int jj = 0; jj < 5; jj++) { float j = j0 + float(jj) - 2.0; float cy = j * 0.5; float ox = mod(j, 2.0) * (SQ3 * 0.5); float i0 = floor((q.x - ox) / SQ3 + 0.5); for (int ii = 0; ii < 3; ii++) { vec2 c = vec2((i0 + float(ii) - 1.0) * SQ3 + ox, cy); if (length(q - c) < 1.0 && cy < bestY) { bestY = cy; bestC = c; } } } float d = length(q - bestC); float bd = 1.0 - d; // this fan's own outer arc for (int jj = 0; jj < 5; jj++) { float j = j0 + float(jj) - 2.0; float cy = j * 0.5; if (cy < bestY - 0.01) { // circles in front cut the fan float ox = mod(j, 2.0) * (SQ3 * 0.5); float i0 = floor((q.x - ox) / SQ3 + 0.5); for (int ii = 0; ii < 3; ii++) { vec2 c = vec2((i0 + float(ii) - 1.0) * SQ3 + ox, cy); bd = min(bd, length(q - c) - 1.0); } } } float rd = abs(fract(d * rings + 0.5) - 0.5) / rings; // nearest arc float ld = min(rd, bd); float e = min(PI * rings * pq * 1.5, 1.0); // ld lives in q-space, so the widths travel there too return vec3(stroke(ld, w * SQ3, pq), stroke(ld, w * SQ3 * 3.0, pq * 2.5), bands(d * rings, e)); } // --------------------------------------------------------------------------- // asanoha - hemp leaf. A triangular lattice of dots where every triangle also // carries a Y from its centroid out to its three corners. Read from one dot's // hexagonal Voronoi cell that is exactly twelve radial spokes: six lattice // edges leaving through the edge midpoints, six Y-arms out to the corners. // The honeycomb itself is NOT part of the pattern - it only fades in as a // containing outline once 'fill' starts blocking in the leaves. vec3 asanoha(vec2 p, float px, float w) { vec2 s = vec2(1.0, SQ3); vec2 a = mod(p, s) - s * 0.5; vec2 b = mod(p - s * 0.5, s) - s * 0.5; vec2 g = (dot(a, a) < dot(b, b)) ? a : b; float th = atan(g.y, g.x); float f = mod(th + PI / 12.0, PI / 6.0) - PI / 12.0; // spokes every 30 deg float sd = length(g) * abs(sin(f)); vec2 n2 = vec2( 0.5, 0.8660254); vec2 n3 = vec2(-0.5, 0.8660254); float hx = max(abs(g.x), max(abs(dot(g, n2)), abs(dot(g, n3)))); float wall = abs(0.5 - hx) + (1.0 - clamp(fill * 3.0, 0.0, 1.0)) * 9.0; float ld = min(sd, wall); float sect = floor(mod(th + PI / 12.0 + TAU, TAU) / (PI / 6.0)); return vec3(stroke(ld, w, px), stroke(ld, w * 3.0, px * 2.5), mod(sect, 2.0)); } // --------------------------------------------------------------------------- // shippou - circles of radius 1/sqrt(2) on a unit grid, so four of them // meet at every cell corner. Band = parity of how many discs cover the point. vec3 shippou(vec2 p, float px, float w) { float R = 0.70710678; vec2 id = floor(p + 0.5); float dmin = 1000.0; float cnt = 0.0; for (int j = 0; j < 3; j++) { for (int i = 0; i < 3; i++) { vec2 c = id + vec2(float(i) - 1.0, float(j) - 1.0); float dd = length(p - c); dmin = min(dmin, abs(dd - R)); cnt += (dd < R) ? 1.0 : 0.0; } } return vec3(stroke(dmin, w, px), stroke(dmin, w * 3.0, px * 2.5), mod(cnt, 2.0)); } // --------------------------------------------------------------------------- // yagasuri - columns of arrow fletching, each column flipped against its // neighbours so the chevrons interlock. vec3 yagasuri(vec2 p, float px, float w) { float n = 2.0; // stripes per unit float k = 1.2; // chevron slope float id = floor(p.x + 0.5); float lx = p.x - id; float dir = mod(id, 2.0) * 2.0 - 1.0; float x = (p.y * dir + abs(lx) * k) * n; float gm = n * sqrt(1.0 + k * k); // |grad x| in pattern units float sd = abs(fract(x + 0.5) - 0.5) / gm; float cd = abs(abs(lx) - 0.5); // column rule float ld = min(sd, cd); float e = min(PI * gm * px * 1.5, 1.0); return vec3(stroke(ld, w, px), stroke(ld, w * 3.0, px * 2.5), bands(x, e)); } // --------------------------------------------------------------------------- void main() { // Phase positions, not accumulators. 'scroll'/'twist' are the automation // targets and wrap at 0/1 onto an exact lattice period, so a host ramp can // run through them forever; 'flow'/'spin' just add free-run drift for when // nothing is automated. float sc = fract(scroll + TIME * flow * 0.35); float sp = fract(twist + TIME * spin * 0.05); float kick = clamp(hit, 0.0, 1.0); // beat envelope, driven by host float m = clamp(morph, 0.0, 1.0) * 3.0; // 0..3 spans the four wagara vec2 uv = (gl_FragCoord.xy - 0.5 * RENDERSIZE.xy) / RENDERSIZE.y; float cells = mix(3.0, 13.0, density) * (1.0 - 0.10 * kick); vec2 p = rot2(TAU * sp) * uv * cells; float px = max(cells / RENDERSIZE.y, 0.00001); float w = max(mix(0.012, 0.055, weight), px * 1.15); // Sharpened handover: two patterns only co-exist over the middle 40% of // each step, so most of the knob sits on a clean pattern instead of a // double-exposure of two line fields. float w0 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m )) * 2.5 - 0.75, 0.0, 1.0)); float w1 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m - 1.0)) * 2.5 - 0.75, 0.0, 1.0)); float w2 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m - 2.0)) * 2.5 - 0.75, 0.0, 1.0)); float w3 = smoothstep(0.0, 1.0, clamp((1.0 - abs(m - 3.0)) * 2.5 - 0.75, 0.0, 1.0)); // Each scroll offset is exactly one lattice period of that pattern, so the // phase wrap is invisible. KA rescales asanoha's cell (12 spokes per cell // reads far denser than a fan or a ring) to match the others by eye. const float KA = 0.60; vec3 fld = vec3(0.0); if (w0 > 0.002) fld += w0 * seigaiha(p + vec2(0.0, sc * 0.5773503), px, w); if (w1 > 0.002) fld += w1 * asanoha (p * KA + vec2(0.0, sc * SQ3 ), px * KA, w * KA); if (w2 > 0.002) fld += w2 * shippou (p + vec2(0.0, sc ), px, w); if (w3 > 0.002) fld += w3 * yagasuri(p + vec2(0.0, sc * 0.5 ), px, w); float cov = clamp(max(fld.x, fld.z * fill * 0.92), 0.0, 1.0); float halo = fld.y; // ---- kamon: petal crown + disc that inverts the field ------------- float r = length(uv); float th = atan(uv.y, uv.x) - TAU * sp * 2.0; float aa = 1.5 / RENDERSIZE.y; float rc = mix(0.10, 0.42, crestSize) * (1.0 + 0.06 * kick); float petalR = rc * (1.0 + 0.30 * (0.5 + 0.5 * cos(16.0 * th))); float ringM = smoothstep(rc * 0.99 + aa, rc * 0.99 - aa, r) * smoothstep(rc * 0.86 - aa, rc * 0.86 + aa, r); float petalM = smoothstep(petalR + aa, petalR - aa, r) * smoothstep(rc * 0.99 - aa, rc * 0.99 + aa, r); float seedM = smoothstep(rc * 0.14 + aa, rc * 0.14 - aa, r); float discM = smoothstep(rc * 0.87 + aa, rc * 0.87 - aa, r); // the emblem reads as a stamp, so it goes opaque early and 'crest' is // really a fade-in rather than a permanent transparency float crestA = smoothstep(0.0, 0.45, crest); cov = mix(cov, 1.0 - cov, discM * crestA); // stencil polarity flip // ---- palette ---------------------------------------------------------- vec3 inkC, paperC, accC; if (palette == 0) { // indigo on washi inkC = vec3(0.055, 0.125, 0.330); paperC = vec3(0.930, 0.910, 0.850); accC = vec3(0.860, 0.720, 0.360); } else if (palette == 1) { // vermilion inkC = vec3(0.780, 0.150, 0.090); paperC = vec3(0.960, 0.930, 0.860); accC = vec3(0.930, 0.780, 0.300); } else if (palette == 2) { // sumi ink inkC = vec3(0.060, 0.060, 0.070); paperC = vec3(0.900, 0.870, 0.800); accC = vec3(0.650, 0.160, 0.120); } else { // gold on lacquer inkC = vec3(0.880, 0.700, 0.280); paperC = vec3(0.045, 0.035, 0.050); accC = vec3(0.960, 0.900, 0.720); } vec3 nHue = hueRGB(hue); inkC = mix(inkC, nHue, neon); paperC = mix(paperC, nHue * 0.05, neon); accC = mix(accC, hueRGB(hue + 0.12), neon); if (inkOnly) { paperC = vec3(0.0); inkC /= max(max(inkC.r, max(inkC.g, inkC.b)), 0.001); } // ---- compose ---------------------------------------------------------- vec3 col = mix(paperC, inkC, cov); col = mix(col, inkC, ringM * crestA); col = mix(col, accC, max(petalM, seedM) * crestA); float ground = dot(paperC, vec3(0.299, 0.587, 0.114)); float dark = smoothstep(0.35, 0.05, ground); col = mix(col, inkC, halo * glow * 0.30 * (1.0 - dark)); // ink bleed on paper col += accC * halo * glow * 0.85 * dark; // bloom on dark // beat shockwave float rad = (1.0 - kick) * 1.15; float dsh = (r - rad) * 9.0; float shock = exp(-dsh * dsh) * kick; col = mix(col, accC, clamp(shock, 0.0, 1.0) * 0.85); // Energy pushes contrast rather than raw brightness: a straight gain just // blows a near-white washi ground out to flat paper. float punch = clamp(energy + kick, 0.0, 1.0); col = mix(col, clamp((col - 0.5) * (1.0 + 0.65 * punch) + 0.5, 0.0, 1.0), 0.85); col += accC * punch * 0.25 * dark; col *= 1.0 - 0.28 * dot(uv, uv); col = col / (1.0 + max(col - 1.0, 0.0)); col = clamp(col, 0.0, 1.0); float alpha = inkOnly ? clamp(max(cov, halo * glow), 0.0, 1.0) : 1.0; gl_FragColor = vec4(col, alpha); }
forking #14iy1g86 — lineage will be recorded.
renders in YOUR browser. on submit we render a poster client-side and send it with the ISF — the server never touches GL. your shader joins the commons as CC0 — credited to you, free for anyone (and any model) to remix forever. that's the whole point. it's permanent: once it's public, CC0 has no take-backs. by submitting you attest you have the right to release it as CC0.