{
  "license": "CC0-1.0",
  "shader": {
    "id": "0brim4hs",
    "title": "Waving Stick Figure",
    "license": "CC0-1.0",
    "provenance_type": "user_upload",
    "made_with": "glslop",
    "status": "active",
    "compile_ok": true,
    "render_hash": "0018181818181400",
    "tags": [],
    "parents": [],
    "author": "jab6zg50vals",
    "author_name": "ogcraig",
    "author_url": "/u/ogcraig",
    "created_at": "2026-10-22T16:11:26.591Z",
    "poster_url": "/posters/0brim4hs.webp",
    "stats": {
      "slops": 0,
      "views": 11,
      "forks": 0
    },
    "self": "/api/v1/shaders/0brim4hs",
    "isf": "/*{\n  \"DESCRIPTION\": \"Restroom-sign pictogram: white standing figure on a rounded black tile. Articulated waving arm (shoulder, elbow, wrist) ending in the pictogram's rounded nub, with an elbow-led lift, follow-through and head follow. Auto loop or manual. Also color cycle, glow, outline, pulse and jumping-jack swing for live use.\",\n  \"CREDIT\": \"Craig\",\n  \"ISFVSN\": \"2\",\n  \"CATEGORIES\": [\"generator\"],\n  \"INPUTS\": [\n    { \"NAME\": \"speed\",       \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0,  \"MAX\": 4.0 },\n    { \"NAME\": \"hue\",         \"TYPE\": \"float\", \"DEFAULT\": 0.5, \"MIN\": 0.0,  \"MAX\": 1.0 },\n    { \"NAME\": \"colorCycle\",  \"LABEL\": \"Color Cycle\",  \"TYPE\": \"float\", \"DEFAULT\": 0.0, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"figureColor\", \"LABEL\": \"Figure Color\", \"TYPE\": \"color\", \"DEFAULT\": [1.0, 1.0, 1.0, 1.0] },\n    { \"NAME\": \"tileColor\",   \"LABEL\": \"Tile Color\",   \"TYPE\": \"color\", \"DEFAULT\": [0.0, 0.0, 0.0, 1.0] },\n    { \"NAME\": \"outerColor\",  \"LABEL\": \"Outer Color\",  \"TYPE\": \"color\", \"DEFAULT\": [1.0, 1.0, 1.0, 1.0] },\n    { \"NAME\": \"showTile\",    \"LABEL\": \"Show Tile\",    \"TYPE\": \"bool\",  \"DEFAULT\": 1 },\n    { \"NAME\": \"zoom\",        \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.25, \"MAX\": 3.0 },\n\n    { \"NAME\": \"waveAuto\",    \"LABEL\": \"Auto Wave Loop\", \"TYPE\": \"bool\",  \"DEFAULT\": 1 },\n    { \"NAME\": \"waveStyle\",   \"LABEL\": \"Wave Style\",     \"TYPE\": \"long\",  \"DEFAULT\": 0,\n      \"VALUES\": [0, 1, 2], \"LABELS\": [\"Elbow Wave\", \"Wrist Flick\", \"Big Wave\"] },\n    { \"NAME\": \"waveRaise\",   \"LABEL\": \"Arm Raise\",      \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"waveCycle\",   \"LABEL\": \"Loop Length (s)\",\"TYPE\": \"float\", \"DEFAULT\": 6.0, \"MIN\": 2.0, \"MAX\": 20.0 },\n    { \"NAME\": \"waveSpeed\",   \"LABEL\": \"Wave Speed\",     \"TYPE\": \"float\", \"DEFAULT\": 1.6, \"MIN\": 0.0, \"MAX\": 5.0 },\n    { \"NAME\": \"waveAmount\",  \"LABEL\": \"Wave Amount\",    \"TYPE\": \"float\", \"DEFAULT\": 0.6, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"bodyFollow\",  \"LABEL\": \"Head Follow\",    \"TYPE\": \"float\", \"DEFAULT\": 0.5, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"waveLeft\",    \"LABEL\": \"Wave Other Arm\", \"TYPE\": \"bool\",  \"DEFAULT\": 0 },\n\n    { \"NAME\": \"pulse\",       \"TYPE\": \"float\", \"DEFAULT\": 0.0, \"MIN\": 0.0,  \"MAX\": 1.0 },\n    { \"NAME\": \"armSwing\",    \"LABEL\": \"Arm Swing\",    \"TYPE\": \"float\", \"DEFAULT\": 0.0, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"legSwing\",    \"LABEL\": \"Leg Swing\",    \"TYPE\": \"float\", \"DEFAULT\": 0.0, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"glow\",        \"TYPE\": \"float\", \"DEFAULT\": 0.0, \"MIN\": 0.0,  \"MAX\": 1.0 },\n    { \"NAME\": \"outlineMix\",  \"LABEL\": \"Outline Only\", \"TYPE\": \"float\", \"DEFAULT\": 0.0, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"lineWidth\",   \"LABEL\": \"Line Width\",   \"TYPE\": \"float\", \"DEFAULT\": 0.3, \"MIN\": 0.0, \"MAX\": 1.0 }\n  ]\n}*/\n\n// All geometry is measured off the 1000x1000 reference, in a y-up pixel frame\n// (Y = 1000 - imageY), so the numbers map straight back to the photo.\n// The figure is built to wave its screen-right arm; \"Wave Other Arm\" mirrors\n// the sample point (the reference is symmetric about x = 491).\n\n#define PI      3.14159265\n#define HALF_PI 1.5707963\n\nmat2 rot(float a) {\n    float c = cos(a), s = sin(a);\n    return mat2(c, -s, s, c);\n}\n\n// iq rounded box with per-corner radii: r = (topRight, bottomRight, topLeft, bottomLeft)\nfloat sdRoundBox(vec2 p, vec2 b, vec4 r) {\n    r.xy = (p.x > 0.0) ? r.xy : r.zw;\n    r.x  = (p.y > 0.0) ? r.x  : r.y;\n    vec2 q = abs(p) - b + r.x;\n    return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;\n}\n\n// Box defined by min/max corners\nfloat boxMM(vec2 p, vec2 mn, vec2 mx, vec4 r) {\n    vec2 c = 0.5 * (mn + mx);\n    vec2 h = 0.5 * (mx - mn);\n    return sdRoundBox(p - c, h, r);\n}\n\nfloat sdSeg(vec2 p, vec2 a, vec2 b) {\n    vec2 pa = p - a, ba = b - a;\n    float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);\n    return length(pa - ba * h);\n}\n\nfloat dot2(vec2 v) { return dot(v, v); }\n\n// iq exact distance to a quadratic Bezier (A -> C, control B)\nfloat sdBezier(vec2 pos, vec2 A, vec2 B, vec2 C) {\n    vec2 a = B - A;\n    vec2 b = A - 2.0 * B + C;\n    vec2 c = a * 2.0;\n    vec2 d = A - pos;\n    float kk = 1.0 / dot(b, b);\n    float kx = kk * dot(a, b);\n    float ky = kk * (2.0 * dot(a, a) + dot(d, b)) / 3.0;\n    float kz = kk * dot(d, a);\n    float res;\n    float pp = ky - kx * kx;\n    float p3 = pp * pp * pp;\n    float qq = kx * (2.0 * kx * kx - 3.0 * ky) + kz;\n    float h = qq * qq + 4.0 * p3;\n    if (h >= 0.0) {\n        h = sqrt(h);\n        vec2 x = (vec2(h, -h) - qq) / 2.0;\n        vec2 uv = sign(x) * pow(abs(x), vec2(1.0 / 3.0));\n        float t = clamp(uv.x + uv.y - kx, 0.0, 1.0);\n        res = dot2(d + (c + b * t) * t);\n    } else {\n        float z = sqrt(-pp);\n        float v = acos(qq / (pp * z * 2.0)) / 3.0;\n        float m = cos(v);\n        float n = sin(v) * 1.732050808;\n        vec3 t = clamp(vec3(m + m, -n - m, n - m) * z - kx, 0.0, 1.0);\n        res = min(dot2(d + (c + b * t.x) * t.x), dot2(d + (c + b * t.y) * t.y));\n    }\n    return sqrt(res);\n}\n\nfloat smin(float a, float b, float k) {\n    k = max(k, 0.001);\n    float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);\n    return mix(b, a, h) - k * h * (1.0 - h);\n}\n\n// Direction for a right-side arm segment.\n// 0 = pointing outward, -PI/2 = hanging down, +PI/2 = straight up.\nvec2 armDir(float a) {\n    return vec2(cos(a), sin(a));\n}\n\n// Ease with a small late overshoot, so the arm \"arrives\" instead of stopping dead\nfloat liftCurve(float x) {\n    x = clamp(x, 0.0, 1.0);\n    return smoothstep(0.0, 0.8, x) + 0.07 * sin(PI * clamp((x - 0.6) / 0.4, 0.0, 1.0));\n}\n\nstruct Pose {\n    float jack;      // jumping-jack outward swing (both arms when not waving)\n    float aLeg;      // leg swing\n    float lift;      // 0..1 how far the shoulder has lifted the waving arm (eased, may overshoot)\n    float fold;      // 0..1 how far the elbow has folded\n    float upper;     // waving upper-arm angle\n    float fore;      // waving forearm absolute angle\n    float wrist;     // waving nub angle (absolute)\n    float headTilt;  // head roll around the neck\n};\n\nfloat figure(vec2 p, Pose P) {\n    // Head, rolled around the neck\n    vec2 N = vec2(491.0, 772.0);\n    vec2 hp = rot(P.headTilt) * (p - N) + N;\n    float head = length(hp - vec2(491.0, 854.0)) - 67.0;\n\n    float lift = clamp(P.lift, 0.0, 1.0);\n\n    // Shoulder yoke with rounded top corners. On the waving side the shoulder\n    // cap tucks in and tightens as the arm lifts, so the raised arm becomes the\n    // shoulder line instead of sprouting out from under a hump.\n    float xR = mix(648.0, 576.0, lift);\n    float rR = mix(80.0, 34.0, lift);\n    float yoke = boxMM(p, vec2(333.0, 600.0), vec2(xR, 772.0), vec4(rR, 0.0, 80.0, 0.0));\n\n    // Below the armpit line outside the torso the arms own the space.\n    // On the waving side the cut rises as the arm lifts; the smooth blend\n    // with the arm below turns it into a clean armpit.\n    float cutL = boxMM(p, vec2(-1000.0, -1000.0), vec2(407.0, 668.0), vec4(0.0));\n    float cutY = 668.0 + 40.0 * lift;\n    float cutR = boxMM(p, vec2(576.0, -1000.0), vec2(2000.0, cutY), vec4(0.0));\n    yoke = max(yoke, -min(cutL, cutR));\n\n    // Torso block down to the crotch\n    float torso = boxMM(p, vec2(407.0, 452.0), vec2(576.0, 700.0), vec4(0.0));\n    float body = min(yoke, torso);\n\n    // --- Resting arm (screen left): straight capsule, jumping-jack capable ---\n    vec2 SL = vec2(363.0, 700.0);\n    float aRest = -HALF_PI + P.jack;\n    vec2 dL = vec2(-cos(aRest), sin(aRest));\n    float armL = sdSeg(p, SL, SL + 218.0 * dL) - 30.0;\n\n    // --- Waving arm (screen right): shoulder -> elbow -> wrist -> nub ---\n    // The shoulder joint slides up and in as the arm lifts, so the arm comes out\n    // of the top of the shoulder instead of the armpit.\n    vec2 S = mix(vec2(619.0, 700.0), vec2(578.0, 744.0), smoothstep(0.0, 0.6, lift));\n    vec2 E = S + 115.0 * armDir(P.upper);\n    // Forearm foreshortens a touch when folded up (tilting toward camera),\n    // which keeps the nub inside the tile.\n    vec2 W = E + mix(103.0, 84.0, P.fold) * armDir(P.fore);\n    // Rounded nub: a short tip past the wrist that trails the swing. At rest it\n    // has zero length, so the arm ends exactly like the reference's round hand.\n    vec2 T = W + 30.0 * P.fold * armDir(P.wrist);\n\n    // Even-width tube: straight upper arm, then the forearm and nub as one\n    // smooth curve (Bezier through the wrist), so the flick bends the forearm\n    // like rubber hose instead of kinking at a joint. At rest T == W and the\n    // curve is a straight capsule, identical to the reference arm.\n    float dUpper = sdSeg(p, S, E) - 29.0;\n    float dFore  = sdBezier(p, E, W, T) - 29.0;\n    float armR = smin(dUpper, dFore, 4.0 * P.fold + 0.001);\n\n    // Blend the lifted arm into the shoulder/armpit; razor sharp at rest\n    float k = 0.5 + 24.0 * lift;\n    float d = smin(body, armR, k);\n    d = min(d, armL);\n    d = min(d, head);\n\n    // Legs (rounded feet), swing outward from the hip\n    vec2 lpL = rot(-P.aLeg) * (p - vec2(445.0, 470.0)) + vec2(445.0, 470.0);\n    vec2 lpR = rot( P.aLeg) * (p - vec2(537.0, 470.0)) + vec2(537.0, 470.0);\n    float legL = boxMM(lpL, vec2(407.0, 115.0), vec2(484.0, 470.0), vec4(0.0, 38.5, 0.0, 38.5));\n    float legR = boxMM(lpR, vec2(498.0, 115.0), vec2(576.0, 470.0), vec4(0.0, 39.0, 0.0, 39.0));\n    d = min(d, min(legL, legR));\n    return d;\n}\n\nvoid main() {\n    float t = TIME * speed;\n\n    // Aspect-correct coords, mapped into the reference's 1000-unit frame\n    vec2 p = isf_FragNormCoord - 0.5;\n    p.x *= RENDERSIZE.x / RENDERSIZE.y;\n    float upp = 1000.0 / (RENDERSIZE.y * zoom);   // reference units per screen pixel (for AA)\n    vec2 q = p * 1000.0 / zoom + 500.0;\n\n    // Rounded black tile\n    float dTile = sdRoundBox(q - vec2(500.0), vec2(468.0), vec4(95.0));\n    float tileM = showTile ? clamp(0.5 - dTile / upp, 0.0, 1.0) : 1.0;\n\n    // ---------------- Wave choreography ----------------\n    // Auto loop: rest -> lift -> wave -> lower -> rest\n    float ph = fract(t / waveCycle);\n    float autoRaise = smoothstep(0.06, 0.24, ph) * (1.0 - smoothstep(0.80, 0.96, ph));\n    float raise = waveAuto ? autoRaise * waveRaise : waveRaise;\n\n    // Elbow leads: the shoulder lifts the upper arm while the forearm dangles,\n    // then the forearm swings up into the wave. Reversed on the way down.\n    float lift = liftCurve(raise);\n    float fold = smoothstep(0.35, 1.0, raise);\n    float env  = smoothstep(0.78, 1.0, raise);     // waving only once the arm is up\n\n    // Style presets: (upper angle, elbow bend, forearm swing, wrist flap, shoulder swing)\n    float style = float(waveStyle);\n    vec2  pose  = vec2(0.16, 1.22);                // elbow wave: upper arm just above level, forearm upright\n    vec3  amp   = vec3(0.75, 0.30, 0.00);\n    if (style > 0.5 && style < 1.5) {              // wrist flick: arm still, the nub does the work\n        pose = vec2(0.22, 1.12); amp = vec3(0.08, 0.60, 0.00);\n    } else if (style > 1.5) {                      // big wave: open elbow, whole arm arcs from the shoulder\n        pose = vec2(0.08, 0.86); amp = vec3(0.10, 0.30, 0.27);\n    }\n\n    // Oscillators: forearm drives, the wrist lags behind it (follow-through),\n    // and the upper arm counter-moves a little to keep the elbow alive.\n    float w   = t * waveSpeed * 2.0 * PI;\n    float A   = waveAmount * env;\n    float osc = sin(w);\n    float lag = sin(w - 1.1);\n\n    float jackSwing = 0.5 + 0.5 * sin(t * 2.5);    // 0..1, always outward\n    float jack = armSwing * 0.9 * jackSwing;\n\n    Pose P;\n    P.jack  = jack;\n    P.aLeg  = legSwing * 0.35 * jackSwing;\n    P.lift  = lift;\n    P.fold  = fold;\n\n    float rest = -HALF_PI + jack * (1.0 - fold);\n    P.upper = mix(rest, pose.x, lift) + A * (amp.z - 0.25 * amp.x) * osc;\n    // Forearm hangs with gravity (slightly out from the elbow) until it folds up\n    float foreHang = min(P.upper, -HALF_PI + 0.3 + jack);\n    float foreUp   = P.upper + pose.y + A * amp.x * osc;\n    P.fore  = mix(foreHang, foreUp, fold);\n    // Nub tips slightly inward when up, plus the lagging flick\n    P.wrist = P.fore - 0.12 * fold + A * amp.y * lag;\n\n    // Head leans gently away from the raised arm and nods with the wave\n    P.headTilt = bodyFollow * (0.09 * clamp(lift, 0.0, 1.0) + 0.035 * A * sin(w - 1.8));\n\n    // ---------------- Figure ----------------\n    float s = 1.0 + pulse * 0.08 * sin(t * 3.0);\n    vec2 c = vec2(491.0, 500.0);\n    vec2 fq = (q - c) / s + c;\n    if (waveLeft) fq.x = 982.0 - fq.x;             // mirror about the figure's centre line\n\n    float dFig = figure(fq, P) * s;\n\n    // Fill vs outline\n    float lw    = mix(3.0, 40.0, lineWidth);\n    float mFill = clamp(0.5 - dFig / upp, 0.0, 1.0);\n    float mLine = clamp(0.5 - (abs(dFig) - lw * 0.5) / upp, 0.0, 1.0);\n    float m     = mix(mFill, mLine, outlineMix);\n\n    // Color: solid figure color blended toward the reference cosine palette\n    vec3 pal = 0.5 + 0.5 * cos(t + q.y * 0.006 + q.x * 0.002 + vec3(0.0, 2.0, 4.0) + hue * 6.2831);\n    vec3 fig = mix(figureColor.rgb, pal, colorCycle);\n\n    // Soft halo around the figure\n    float g = glow * exp(-max(dFig, 0.0) / 60.0) * 0.8;\n\n    vec3 inside = tileColor.rgb + fig * g;\n    inside = mix(inside, fig, m);\n\n    vec3 col = mix(outerColor.rgb, inside, tileM);\n    gl_FragColor = vec4(col, 1.0);\n}"
  }
}