{
  "name": "Dust Devil",
  "dimension": "3d",
  "slot": "action_bar",
  "width": 256,
  "height": 128,
  "textBaseline": 128,
  "placement": {
    "x": 0,
    "y": 0
  },
  "shapes": [],
  "texts": [],
  "heads": [],
  "images": [],
  "bars": [],
  "triggers": [],
  "world": {
    "glsl": "float dustNoise(vec3 p) {\n    vec3 cell = floor(p);\n    vec3 f = fract(p);\n    f = f * f * (3.0 - 2.0 * f);\n    float n = dot(cell, vec3(1.0, 57.0, 113.0));\n    vec4 a = fract(sin(vec4(n) + vec4(0.0, 1.0, 57.0, 58.0)) * 43758.5453);\n    vec4 b = fract(sin(vec4(n) + vec4(113.0, 114.0, 170.0, 171.0)) * 43758.5453);\n    vec4 z = mix(a, b, f.z);\n    return mix(mix(z.x, z.y, f.x), mix(z.z, z.w, f.x), f.y);\n}\nfloat dustFalloff(float x) {\n    float f = max(0.0, 1.0 - x * x);\n    return f * f;\n}\nvec4 object(vec3 ro, vec3 rd, float t0, float t1, float time, vec3 light, inout float depth) {\n    vec3 col = vec3(0.0);\n    float alpha = 0.0;\n    float stepSize = (t1 - t0) / 30.0;\n    bool foundDepth = false;\n    vec2 lean = vec2(0.073 * sin(time * 0.65), 0.057 * cos(time * 0.55));\n    vec2 crook = vec2(0.047 * cos(time * 0.43), 0.033 * sin(time * 0.38));\n    mat2 spin = rpw_rot(time * 1.6);\n    float phase = time * 5.2;\n    float gritT = t1 + 1.0;\n    vec3 gritColor = vec3(0.0);\n    for (int j = 0; j < 9; j++) {\n        float seed = float(j);\n        float h = fract(seed * 0.618 + time * 0.095);\n        float y = -0.72 + h * 1.42;\n        float orbit = 0.105 + 0.44 * h * h + 0.035 * sin(seed * 7.1);\n        float angle = seed * 9.73 - time * (2.2 - h * 0.8);\n        vec2 centerXZ = vec2(cos(angle), sin(angle)) * orbit + lean * h * h + crook * h * (1.0 - h);\n        vec3 center = vec3(centerXZ.x, y, centerXZ.y);\n        float radius = (0.006 + 0.005 * fract(seed * 0.37)) * smoothstep(0.0, 0.07, h) * (1.0 - smoothstep(0.91, 1.0, h));\n        vec3 oc = ro - center;\n        float b = dot(oc, rd);\n        float discriminant = b * b - dot(oc, oc) + radius * radius;\n        if (discriminant > 0.0) {\n            float hit = -b - sqrt(discriminant);\n            if (hit > t0 && hit < gritT) {\n                gritT = hit;\n                vec3 normal = (oc + rd * hit) / max(radius, 0.0001);\n                gritColor = vec3(0.29, 0.20, 0.11) * (0.65 + 0.4 * max(0.0, dot(normal, normalize(vec3(0.4, 0.8, 0.3))))) * light;\n            }\n        }\n    }\n    for (int i = 0; i < 30; i++) {\n        float t = t0 + (float(i) + 0.5) * stepSize;\n        if (t > gritT) {\n            col += (1.0 - alpha) * gritColor;\n            alpha = 1.0;\n            if (!foundDepth) depth = gritT;\n            break;\n        }\n        vec3 p = ro + rd * t;\n        float h = clamp((p.y + 0.78) / 1.53, 0.0, 1.0);\n        vec2 q = p.xz - lean * h * h - crook * h * (1.0 - h);\n        float r = length(q);\n        float radius = 0.053 + 0.452 * h * h;\n        float vertical = smoothstep(-0.82, -0.72, p.y) * (1.0 - smoothstep(0.58, 0.81, p.y));\n        float baseMask = dustFalloff((p.y + 0.73) / 0.13);\n        if ((r < radius + 0.17 && vertical > 0.001) || (r < 0.54 && baseMask > 0.001)) {\n            float angle = atan(q.y, q.x);\n            vec2 spinning = rpw_rot(-h * 6.0) * (spin * q);\n            float noiseValue = dustNoise(vec3(spinning.x * 19.0, p.y * 12.0 - time, spinning.y * 19.0));\n            float helix = sin(angle * 2.0 + h * 37.0 - phase + noiseValue * 1.4);\n            float wave = 0.019 * helix + (noiseValue - 0.5) * 0.11;\n            float wall = dustFalloff((r - radius - wave) / (0.060 + h * 0.085));\n            float core = dustFalloff(r / max(radius * 0.95, 0.035));\n            float ribbon = max(0.0, sin(angle * 2.0 + h * 43.0 - time * 5.0));\n            ribbon = ribbon * ribbon * ribbon * ribbon;\n            float outer = dustFalloff((r - radius - 0.076 - wave) / 0.043) * ribbon;\n            float density = vertical * ((wall * (2.6 + 7.6 * noiseValue) + core * 0.95) * (0.73 + 0.27 * helix) + outer * 1.8);\n            density += baseMask * dustFalloff((r - 0.23) / 0.31) * (1.8 + 5.4 * noiseValue);\n            density *= 1.0 - smoothstep(0.86, 0.91, dot(p, p));\n            float facing = dot(q / max(r, 0.01), vec2(0.47, 0.35));\n            float shade = 0.84 + 0.28 * facing + 0.16 * noiseValue;\n            vec3 dust = mix(vec3(0.43, 0.29, 0.16), vec3(0.86, 0.72, 0.49), clamp(h * 0.52 + noiseValue * 0.64, 0.0, 1.0));\n            dust *= shade * light;\n            float optical = density * stepSize;\n            float a = optical / (1.0 + optical);\n            col += (1.0 - alpha) * dust * a;\n            alpha += (1.0 - alpha) * a;\n            if (!foundDepth && alpha > 0.30) { depth = t; foundDepth = true; }\n            if (alpha > 0.985) break;\n        }\n    }\n    if (!foundDepth && alpha < 0.999) depth = clamp(dot(-ro, rd), t0, t1);\n    return vec4(col / max(alpha, 0.0001), alpha);\n}",
    "prompt": "a small swirling tornado of dust and wind",
    "size": 3,
    "summary": "A small sandy tornado sways and swirls with winding dust ribbons, rising grit, and a spreading cloud around its base."
  }
}