228

Worthington Water Splash

High-speed fluid drop impact mechanics capturing the elevated crenellated Worthington splash crown, Rayleigh-Plateau capillary central jet, and pinch-off droplet ballistics.

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

Full Executable Algorithm Code

// 040 - Worthington Water Splash (fluid)
// 1:1 Original algorithm engine source
function createWaterSplash() {
  const CROWN_POINTS = 16;
  const DROPLET_COUNT = 32;
  const dropX = new Float32Array(DROPLET_COUNT);
  const dropY = new Float32Array(DROPLET_COUNT);
  const dropVx = new Float32Array(DROPLET_COUNT);
  const dropVy = new Float32Array(DROPLET_COUNT);
  const dropSize = new Float32Array(DROPLET_COUNT);
  const dropLife = new Float32Array(DROPLET_COUNT);
  return {
    setup() {
      for (let i = 0; i < DROPLET_COUNT; i++) {
        dropLife[i] = -1;
      }
    },
    render(context, timeState, params) {
      const { ctx, width, height } = context;
      const speed = Number(params.splashRate || 1.1);
      const splashHeight = Number(params.splashScale || 1.2);
      const dt = Math.min(timeState.deltaTime, 0.05) * speed;
      const t = timeState.time * speed;
      ctx.fillStyle = "rgba(5, 8, 14, 0.22)";
      ctx.fillRect(0, 0, width, height);
      const cx = width * 0.5;
      const waterLineY = height * 0.65;
      const splashCycle = t * 0.9 % 2.5;
      const rippleCount = 5;
      for (let r = 1; r <= rippleCount; r++) {
        const ripplePhase = (splashCycle + r * 0.4) % 2.5;
        const rippleRadius = ripplePhase * 160;
        const rippleAlpha = Math.max(0, 1 - ripplePhase / 2.5) * 0.6;
        ctx.beginPath();
        ctx.ellipse(cx, waterLineY, rippleRadius, rippleRadius * 0.28, 0, 0, Math.PI * 2);
        ctx.strokeStyle = hsla(195, 90, 65, rippleAlpha);
        ctx.lineWidth = 1.4;
        ctx.stroke();
      }
      if (splashCycle < 1.4) {
        const crownAge = splashCycle / 1.4;
        const crownRadius = (25 + crownAge * 95) * splashHeight;
        const crownH = Math.sin(crownAge * Math.PI) * (110 * splashHeight);
        ctx.beginPath();
        for (let i = 0; i <= CROWN_POINTS; i++) {
          const phi = i / CROWN_POINTS * Math.PI * 2;
          const cuspHeight = crownH * (1 + 0.35 * Math.sin(phi * 8));
          const px = cx + Math.cos(phi) * crownRadius;
          const py = waterLineY - cuspHeight + Math.sin(phi) * (crownRadius * 0.25);
          if (i === 0) ctx.moveTo(px, py);
          else ctx.lineTo(px, py);
        }
        ctx.closePath();
        ctx.fillStyle = hsla(190, 85, 55, 0.25);
        ctx.fill();
        ctx.strokeStyle = hsla(185, 95, 75, 0.85);
        ctx.lineWidth = 2.2;
        ctx.stroke();
        for (let i = 0; i < CROWN_POINTS; i++) {
          const phi = i / CROWN_POINTS * Math.PI * 2;
          const cuspHeight = crownH * (1 + 0.35 * Math.sin(phi * 8));
          const px = cx + Math.cos(phi) * crownRadius;
          const py = waterLineY - cuspHeight + Math.sin(phi) * (crownRadius * 0.25);
          ctx.fillStyle = hsla(180, 100, 85, 0.95);
          ctx.beginPath();
          ctx.arc(px, py, 2.5, 0, Math.PI * 2);
          ctx.fill();
        }
      }
      if (splashCycle > 0.4 && splashCycle < 2.2) {
        const jetAge = (splashCycle - 0.4) / 1.8;
        const jetH = Math.sin(jetAge * Math.PI) * (160 * splashHeight);
        const jetW = Math.max(3, (1 - jetAge) * 16);
        ctx.beginPath();
        ctx.moveTo(cx - jetW * 1.5, waterLineY);
        ctx.quadraticCurveTo(cx - jetW * 0.4, waterLineY - jetH * 0.6, cx, waterLineY - jetH);
        ctx.quadraticCurveTo(cx + jetW * 0.4, waterLineY - jetH * 0.6, cx + jetW * 1.5, waterLineY);
        ctx.fillStyle = hsla(195, 90, 50, 0.45);
        ctx.fill();
        ctx.strokeStyle = hsla(185, 95, 78, 0.9);
        ctx.lineWidth = 2;
        ctx.stroke();
        const topDropY = waterLineY - jetH - 18 * Math.sin(jetAge * Math.PI * 1.5);
        ctx.fillStyle = hsla(180, 95, 82, 0.95);
        ctx.beginPath();
        ctx.arc(cx, topDropY, 5.5 * splashHeight, 0, Math.PI * 2);
        ctx.fill();
      }
      if (splashCycle < 0.1) {
        for (let i = 0; i < DROPLET_COUNT; i++) {
          const angle = Math.random() * Math.PI * 2;
          const v = 80 + Math.random() * 140;
          dropX[i] = cx;
          dropY[i] = waterLineY - 10;
          dropVx[i] = Math.cos(angle) * (v * 0.7);
          dropVy[i] = -Math.abs(Math.sin(angle)) * v - 40;
          dropSize[i] = 1.5 + Math.random() * 3;
          dropLife[i] = 1;
        }
      }
      for (let i = 0; i < DROPLET_COUNT; i++) {
        if (dropLife[i] > 0) {
          dropLife[i] -= dt * 0.7;
          dropVy[i] += 260 * dt;
          dropX[i] += dropVx[i] * dt;
          dropY[i] += dropVy[i] * dt;
          if (dropY[i] > waterLineY) {
            dropLife[i] = 0;
          }
          ctx.fillStyle = hsla(190, 95, 75, dropLife[i]);
          ctx.beginPath();
          ctx.arc(dropX[i], dropY[i], dropSize[i], 0, Math.PI * 2);
          ctx.fill();
        }
      }
    }
  };
}

// Default parameters from content metadata
const defaultParams = [
  {
    "key": "splashRate",
    "label": "Impact Cycle Speed",
    "type": "range",
    "min": 0.4,
    "max": 2.5,
    "step": 0.1,
    "defaultValue": 1.1,
    "description": "Water impact repetition frequency"
  },
  {
    "key": "splashScale",
    "label": "Splash Amplitude",
    "type": "range",
    "min": 0.6,
    "max": 1.8,
    "step": 0.1,
    "defaultValue": 1.2,
    "description": "Crown height & jet ejection reach"
  }
];

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

const instance = window.__art_instances['water-splash'];
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
r(θ)=Rrim[1+Acsin(nθ)],ydrop(t)=y0+vy0t12gt2,λopt4.51djetr(\theta) = R_{\text{rim}} [1 + A_c \sin(n \theta)], \quad y_{\text{drop}}(t) = y_0 + v_{y0} t - \frac{1}{2} g t^2, \quad \lambda_{\text{opt}} \approx 4.51 d_{\text{jet}}
Click to expand
Compact Formula
crown_h = sin(age * π) * 110, jet_h = sin(jetAge * π) * 160, drop_y += vy*dt + 0.5*g*dt^2

Mathematical Tags

#splash #water #worthington #fluid #droplets #hydrodynamics #capillary
Author: Math Art Core Target: 60 FPS

Export & Embed: Worthington Water Splash

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/water-splash" 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! ✨