228

Atmospheric Snowfall

Winter precipitation physics capturing Brownian horizontal flutter, Stokes settling velocities, rotational tumbling, and multi-depth bokeh parallax with 6-pointed hexagonal crystallites.

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

Full Executable Algorithm Code

// 042 - Atmospheric Snowfall (botany)
// 1:1 Original algorithm engine source
function createSnowFall() {
  const FLAKE_COUNT = 450;
  const fx = new Float32Array(FLAKE_COUNT);
  const fy = new Float32Array(FLAKE_COUNT);
  const fRadius = new Float32Array(FLAKE_COUNT);
  const fSpeed = new Float32Array(FLAKE_COUNT);
  const fWobblePhase = new Float32Array(FLAKE_COUNT);
  const fWobbleFreq = new Float32Array(FLAKE_COUNT);
  const fRotation = new Float32Array(FLAKE_COUNT);
  return {
    setup(context) {
      for (let i = 0; i < FLAKE_COUNT; i++) {
        fx[i] = Math.random() * context.width;
        fy[i] = Math.random() * context.height;
        const depth = Math.pow(Math.random(), 2);
        fRadius[i] = 1 + depth * 4.5;
        fSpeed[i] = 25 + depth * 70;
        fWobblePhase[i] = Math.random() * Math.PI * 2;
        fWobbleFreq[i] = 1 + Math.random() * 2.5;
        fRotation[i] = Math.random() * Math.PI * 2;
      }
    },
    render(context, timeState, params) {
      const { ctx, width, height } = context;
      const windForce = Number(params.windDrift || 0.4);
      const flakeDensity = Number(params.flakeDensity || 380);
      const dt = Math.min(timeState.deltaTime, 0.05);
      const t = timeState.time;
      ctx.fillStyle = "#05070e";
      ctx.fillRect(0, 0, width, height);
      const activeCount = Math.min(FLAKE_COUNT, flakeDensity);
      for (let i = 0; i < activeCount; i++) {
        fWobblePhase[i] += fWobbleFreq[i] * dt;
        const drift = Math.sin(fWobblePhase[i]) * 1.2 + windForce * (fRadius[i] * 0.8);
        fRotation[i] += dt * (fWobbleFreq[i] * 0.5);
        fy[i] += fSpeed[i] * dt;
        fx[i] += drift;
        if (fy[i] > height + 10) {
          fy[i] = -10;
          fx[i] = Math.random() * (width + 100) - 50;
        }
        if (fx[i] > width + 20) fx[i] = -20;
        if (fx[i] < -20) fx[i] = width + 20;
        const posX = fx[i];
        const posY = fy[i];
        const r = fRadius[i];
        if (r > 3.2) {
          ctx.save();
          ctx.translate(posX, posY);
          ctx.rotate(fRotation[i]);
          ctx.beginPath();
          for (let k = 0; k < 6; k++) {
            const angle = k / 6 * Math.PI * 2;
            const armLen = r * 1.5;
            ctx.moveTo(0, 0);
            ctx.lineTo(Math.cos(angle) * armLen, Math.sin(angle) * armLen);
            const subAngle1 = angle + 0.5;
            const subAngle2 = angle - 0.5;
            const midX = Math.cos(angle) * (armLen * 0.55);
            const midY = Math.sin(angle) * (armLen * 0.55);
            ctx.moveTo(midX, midY);
            ctx.lineTo(midX + Math.cos(subAngle1) * (r * 0.5), midY + Math.sin(subAngle1) * (r * 0.5));
            ctx.moveTo(midX, midY);
            ctx.lineTo(midX + Math.cos(subAngle2) * (r * 0.5), midY + Math.sin(subAngle2) * (r * 0.5));
          }
          ctx.strokeStyle = "rgba(240, 249, 255, 0.9)";
          ctx.lineWidth = 1;
          ctx.stroke();
          ctx.restore();
        } else {
          const alpha = 0.35 + r / 3.2 * 0.55;
          ctx.fillStyle = hsla(210, 80, 92, alpha);
          ctx.beginPath();
          ctx.arc(posX, posY, r, 0, Math.PI * 2);
          ctx.fill();
        }
      }
    }
  };
}

// Default parameters from content metadata
const defaultParams = [
  {
    "key": "windDrift",
    "label": "Breeze Drift Force",
    "type": "range",
    "min": -1,
    "max": 1.5,
    "step": 0.1,
    "defaultValue": 0.4,
    "description": "Lateral wind flutter velocity"
  },
  {
    "key": "flakeDensity",
    "label": "Snowflake Density",
    "type": "range",
    "min": 100,
    "max": 450,
    "step": 25,
    "defaultValue": 380,
    "description": "Atmospheric snowflake count"
  }
];

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

const instance = window.__art_instances['snow-fall'];
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

medium
Analytical Equation
vz=2r2(ρsρa)g9μ,x(t)=x0+Awsin(ωwt+ϕi)+Fwindr,θflake(t)=ωrtv_z = \frac{2 r^2 (\rho_s - \rho_a) g}{9 \mu}, \quad x(t) = x_0 + A_w \sin(\omega_w t + \phi_i) + F_{\text{wind}} r, \quad \theta_{\text{flake}}(t) = \omega_r t
Click to expand
Compact Formula
drift = sin(phase)*1.2 + wind*r*0.8, fy += speed*dt, crystallite = 6_arm_dendrite(r)

Mathematical Tags

#snow #winter #weather #atmosphere #crystals #botany #nature
Author: Math Art Core Target: 60 FPS

Export & Embed: Atmospheric Snowfall

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/snow-fall" 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! ✨