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