228

Atmospheric Tornado

Severe mesocyclone tornadic vortex simulation modeling hyperbolic funnel cone boundary expansion, Rankine tangential velocity shear, and particle suction updraft.

Playground
60 FPS Canvas 2D
Click + Drag to interact with field
</>

Full Executable Algorithm Code

// 039 - Atmospheric Tornado (fluid)
// 1:1 Original algorithm engine source
function createAtmosphericTornado() {
  const PARTICLE_COUNT = 1200;
  const px = new Float32Array(PARTICLE_COUNT);
  const py = new Float32Array(PARTICLE_COUNT);
  const pz = new Float32Array(PARTICLE_COUNT);
  const pAngle = new Float32Array(PARTICLE_COUNT);
  const pRadius = new Float32Array(PARTICLE_COUNT);
  const pSpeed = new Float32Array(PARTICLE_COUNT);
  return {
    setup() {
      for (let i = 0; i < PARTICLE_COUNT; i++) {
        pz[i] = Math.random();
        pAngle[i] = Math.random() * Math.PI * 2;
        pRadius[i] = 0.2 + Math.random() * 0.8;
        pSpeed[i] = 1.5 + Math.random() * 2.5;
      }
    },
    render(context, timeState, params) {
      const { ctx, width, height } = context;
      const swirlSpeed = Number(params.swirlSpeed || 1.4);
      const funnelWidth = Number(params.funnelWidth || 1);
      const dt = Math.min(timeState.deltaTime, 0.05) * swirlSpeed;
      const t = timeState.time * swirlSpeed;
      ctx.fillStyle = "rgba(5, 7, 12, 0.22)";
      ctx.fillRect(0, 0, width, height);
      const groundX = width * 0.5 + Math.sin(t * 0.6) * (width * 0.06);
      const groundY = height * 0.92;
      const cloudY = height * 0.08;
      const totalH = groundY - cloudY;
      ctx.beginPath();
      const funnelSlices = 40;
      for (let s = 0; s <= funnelSlices; s++) {
        const normZ = s / funnelSlices;
        const curY = groundY - normZ * totalH;
        const curR = (18 + Math.pow(normZ, 2.2) * 160) * funnelWidth;
        const sway = Math.sin(t * 1.5 + normZ * 3) * (40 * normZ);
        const curX = groundX + sway;
        const leftX = curX - curR;
        if (s === 0) ctx.moveTo(leftX, curY);
        else ctx.lineTo(leftX, curY);
      }
      for (let s = funnelSlices; s >= 0; s--) {
        const normZ = s / funnelSlices;
        const curY = groundY - normZ * totalH;
        const curR = (18 + Math.pow(normZ, 2.2) * 160) * funnelWidth;
        const sway = Math.sin(t * 1.5 + normZ * 3) * (40 * normZ);
        const curX = groundX + sway;
        const rightX = curX + curR;
        ctx.lineTo(rightX, curY);
      }
      ctx.closePath();
      ctx.fillStyle = "rgba(14, 25, 45, 0.2)";
      ctx.fill();
      ctx.strokeStyle = "rgba(56, 189, 248, 0.3)";
      ctx.lineWidth = 1.2;
      ctx.stroke();
      for (let i = 0; i < PARTICLE_COUNT; i++) {
        pz[i] += dt * (0.35 + pz[i] * 0.4);
        if (pz[i] > 1) {
          pz[i] = 0;
          pAngle[i] = Math.random() * Math.PI * 2;
          pRadius[i] = 0.2 + Math.random() * 0.8;
        }
        const rNorm = pRadius[i];
        const angularVel = pSpeed[i] / (0.3 + rNorm * 0.7) * 4;
        pAngle[i] += angularVel * dt;
        const funnelR = (20 + Math.pow(pz[i], 2.2) * 170) * funnelWidth;
        const radius = funnelR * rNorm;
        const sway = Math.sin(t * 1.5 + pz[i] * 3) * (40 * pz[i]);
        const centerX = groundX + sway;
        const posY = groundY - pz[i] * totalH;
        const posX = centerX + Math.cos(pAngle[i]) * radius;
        const depth = Math.sin(pAngle[i]);
        const pyDepth = posY + depth * (radius * 0.2);
        px[i] = posX;
        py[i] = pyDepth;
        const depthAlpha = 0.2 + (depth + 1) * 0.35;
        const hue = (195 + pz[i] * 30 + depth * 15) % 360;
        const size = (1.2 + (depth + 1) * 0.8) * (1 + (1 - pz[i]) * 0.6);
        ctx.fillStyle = hsla(hue, 85, 65 + depth * 15, depthAlpha);
        ctx.fillRect(posX - size * 0.5, pyDepth - size * 0.5, size, size);
      }
    }
  };
}

// Default parameters from content metadata
const defaultParams = [
  {
    "key": "swirlSpeed",
    "label": "Vortex Rotation Rate",
    "type": "range",
    "min": 0.5,
    "max": 3,
    "step": 0.1,
    "defaultValue": 1.4,
    "description": "Angular vortex velocity multiplier"
  },
  {
    "key": "funnelWidth",
    "label": "Funnel Cone Aperture",
    "type": "range",
    "min": 0.5,
    "max": 2,
    "step": 0.1,
    "defaultValue": 1,
    "description": "Hyperbolic cone boundary scaling"
  }
];

if (!window.__art_instances) window.__art_instances = {};
if (!window.__art_instances['atmospheric-tornado']) {
  const inst = typeof createAtmosphericTornado === 'function' ? createAtmosphericTornado() : null;
  if (inst && inst.setup) {
    inst.setup({ ctx, width, height, dpr: 1, aspectRatio: width / height }, defaultParams);
  }
  window.__art_instances['atmospheric-tornado'] = inst;
}

const instance = window.__art_instances['atmospheric-tornado'];
if (instance && instance.render) {
  instance.render(
    { ctx, width, height, dpr: 1, aspectRatio: width / height },
    { time, deltaTime: dt, frameCount: Math.floor(time * 60), fps: 60 },
    defaultParams
  );
}
Edit in Interactive Playground Zero Dependencies • Standalone Canvas 2D
ƒ

Mathematical Formulation

high
Analytical Equation
vθ(r)={ωrrRcΓ2πrr>Rc,R(z)=R0+αz2.2,vz(z)=v0(1+βz)v_\theta(r) = \begin{cases} \omega r & r \le R_c \\ \frac{\Gamma}{2\pi r} & r > R_c \end{cases}, \quad R(z) = R_0 + \alpha z^{2.2}, \quad v_z(z) = v_0 \left(1 + \beta z\right)
Click to expand
Compact Formula
v_theta = speed / (0.3 + r*0.7), R_funnel = 20 + z^2.2 * 170, sway = sin(1.5t + 3z)*40z

Mathematical Tags

#tornado #vortex #rankine #fluid #meteorology #atmosphere #weather
Author: Math Art Core Target: 60 FPS

Export & Embed: Atmospheric Tornado

4K PNG Snapshot

High-resolution single frame render

WebM Video (5s Loop)

60 FPS browser-captured stream

Standalone JS Script

Complete executable Canvas 2D algorithm

HTML Iframe Embed

<iframe src="https://art.fazleyrabbi.xyz/embed/atmospheric-tornado" width="500" height="500" frameborder="0" loading="lazy"></iframe>
ESC
↑↓ Navigate Select
110 Mathematical Artworks
Made by Fazley Rabbi

Support the Project

Keep mathematical creative coding alive & open source

Or via Direct Payoneer ($0 Fee)
Payoneer Customer ID: $0 Fee Direct
24076084

💡 Payoneer app: Go to Pay → Pay to recipient → Enter ID 24076084.

Thank you for supporting generative mathematical animations! ✨