97

Moonlit Ocean & Lunar Rays

Nocturnal atmospheric optical simulation of a silver full moon casting volumetric moonbeams through cirrus cloud breaks onto an undulating ocean. Computes lunar atmospheric scattering, a specular silver glitter wake, background star scintillation, and glowing cyan bioluminescent phytoplankton.

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

Full Executable Algorithm Code

// 083 - Moonlit Ocean & Lunar Rays (physics)
// 1:1 Original algorithm engine source
function createMoonlitOceanRays() {
  const STAR_COUNT = 130;
  const BIOLUM_COUNT = 55;
  const stars = [];
  const biolumParticles = [];

  function initNightSky() {
    stars.length = 0;
    for (let i = 0; i < STAR_COUNT; i++) {
      stars.push({
        x: Math.random(),
        y: Math.random() * 0.62,
        r: 0.4 + Math.random() * 1.4,
        phase: Math.random() * Math.PI * 2,
        isBright: Math.random() < 0.12,
      });
    }
    biolumParticles.length = 0;
    for (let i = 0; i < BIOLUM_COUNT; i++) {
      biolumParticles.push({
        x: Math.random(),
        y: 0.64 + Math.random() * 0.35,
        vx: (Math.random() - 0.5) * 0.0006,
        vy: (Math.random() - 0.5) * 0.0004,
        r: 0.8 + Math.random() * 2.0,
        phase: Math.random() * Math.PI * 2,
      });
    }
  }

  return {
    setup() {
      initNightSky();
    },
    render(context, timeState, params) {
      const { ctx, width, height } = context;
      const speed = Number(params.speed ?? 1.0);
      const beamIntensity = Number(params.beamIntensity ?? 1.2);
      const moonGlow = Number(params.moonGlow ?? 1.1);
      const oceanGlitter = Number(params.oceanGlitter ?? 1.2);
      const bioluminescence = Number(params.bioluminescence ?? 1.0);

      const t = timeState.time * speed;
      if (stars.length === 0) initNightSky();

      const moonX = width * 0.22;
      const moonY = height * 0.21;
      const moonR = Math.min(width, height) * 0.082;
      const horizonY = height * 0.62;

      // 1. Sky Gradient
      const skyGrad = ctx.createLinearGradient(0, 0, 0, horizonY);
      skyGrad.addColorStop(0, '#010308');
      skyGrad.addColorStop(0.35, '#030818');
      skyGrad.addColorStop(0.7, '#06132c');
      skyGrad.addColorStop(1.0, '#0a1d3d');
      ctx.fillStyle = skyGrad;
      ctx.fillRect(0, 0, width, horizonY);

      // 2. Stars
      ctx.save();
      for (let s = 0; s < stars.length; s++) {
        const star = stars[s];
        const sx = star.x * width;
        const sy = star.y * horizonY;
        if (Math.hypot(sx - moonX, sy - moonY) < moonR * 1.8) continue;
        const twinkle = 0.35 + 0.65 * Math.pow(Math.sin(t * 2.2 + star.phase), 2);
        ctx.fillStyle = `rgba(220, 240, 255, ${twinkle * (star.isBright ? 0.95 : 0.65)})`;
        ctx.beginPath();
        ctx.arc(sx, sy, star.r * (star.isBright ? 1.2 : 0.8), 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.restore();

      // 3. Subtle & Dreamy Atmospheric Moonbeams
      ctx.save();
      ctx.globalCompositeOperation = 'screen';
      const RAY_COUNT = 12;
      const maxRayDist = Math.hypot(width, height) * 1.15;

      for (let i = 0; i < RAY_COUNT; i++) {
        const normI = i / RAY_COUNT;
        const baseAngle = -Math.PI * 0.02 + normI * (Math.PI * 0.44);
        const waveMod = Math.sin(baseAngle * 4.0 + t * 0.2) * 0.05;
        const rayAngle = baseAngle + waveMod;
        const angleDiff = Math.abs(rayAngle - Math.PI * 0.20);
        const centralFactor = Math.pow(Math.max(0, 1 - angleDiff / (Math.PI * 0.3)), 1.4);
        const pulse = 0.75 + 0.25 * Math.sin(i * 1.8 + t * 0.9);
        const beamAlpha = Math.min(0.18, 0.08 * centralFactor * pulse * beamIntensity);

        if (beamAlpha > 0.008) {
          const spreadWidth = 0.085 + (1 - centralFactor) * 0.045;
          const moonbeamGrad = ctx.createRadialGradient(moonX, moonY, moonR * 0.8, moonX, moonY, maxRayDist);
          moonbeamGrad.addColorStop(0, `rgba(215, 238, 255, ${beamAlpha * 1.1})`);
          moonbeamGrad.addColorStop(0.25, `rgba(130, 195, 255, ${beamAlpha * 0.7})`);
          moonbeamGrad.addColorStop(0.65, `rgba(40, 110, 190, ${beamAlpha * 0.25})`);
          moonbeamGrad.addColorStop(1.0, 'rgba(0, 10, 30, 0)');

          ctx.beginPath();
          ctx.moveTo(moonX, moonY);
          ctx.lineTo(moonX + Math.cos(rayAngle - spreadWidth) * maxRayDist, moonY + Math.sin(rayAngle - spreadWidth) * maxRayDist);
          ctx.lineTo(moonX + Math.cos(rayAngle + spreadWidth) * maxRayDist, moonY + Math.sin(rayAngle + spreadWidth) * maxRayDist);
          ctx.closePath();
          ctx.fillStyle = moonbeamGrad;
          ctx.fill();
        }
      }
      ctx.restore();

      // 4. Lunar Atmospheric Halo
      ctx.save();
      ctx.globalCompositeOperation = 'screen';
      const haloGrad = ctx.createRadialGradient(moonX, moonY, moonR * 0.9, moonX, moonY, width * 0.38);
      haloGrad.addColorStop(0, `rgba(215, 238, 255, ${0.28 * moonGlow})`);
      haloGrad.addColorStop(0.2, `rgba(130, 190, 250, ${0.14 * moonGlow})`);
      haloGrad.addColorStop(0.5, `rgba(30, 85, 160, ${0.04 * moonGlow})`);
      haloGrad.addColorStop(1.0, 'rgba(0, 0, 0, 0)');
      ctx.fillStyle = haloGrad;
      ctx.beginPath();
      ctx.arc(moonX, moonY, width * 0.38, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();

      // 5. Realistic Moon Disk
      ctx.save();
      ctx.beginPath();
      ctx.arc(moonX, moonY, moonR, 0, Math.PI * 2);
      ctx.clip();
      const baseMoonGrad = ctx.createRadialGradient(moonX - moonR * 0.3, moonY - moonR * 0.3, moonR * 0.1, moonX, moonY, moonR);
      baseMoonGrad.addColorStop(0, '#ffffff');
      baseMoonGrad.addColorStop(0.3, '#f0f5fa');
      baseMoonGrad.addColorStop(0.55, '#dbe4ee');
      baseMoonGrad.addColorStop(0.85, '#b4c4d6');
      baseMoonGrad.addColorStop(1.0, '#7f93a8');
      ctx.fillStyle = baseMoonGrad;
      ctx.fillRect(moonX - moonR, moonY - moonR, moonR * 2, moonR * 2);
      ctx.restore();

      // 6. Night Ocean & Gentle Shimmering Reflection
      const oceanH = height - horizonY;
      const oceanGrad = ctx.createLinearGradient(0, horizonY, 0, height);
      oceanGrad.addColorStop(0, '#030a17');
      oceanGrad.addColorStop(0.3, '#040d20');
      oceanGrad.addColorStop(0.65, '#020714');
      oceanGrad.addColorStop(1.0, '#010308');
      ctx.fillStyle = oceanGrad;
      ctx.fillRect(0, horizonY, width, oceanH);

      const WAVE_LINES = 28;
      for (let w = 0; w < WAVE_LINES; w++) {
        const normW = w / WAVE_LINES;
        const lineY = horizonY + Math.pow(normW, 1.4) * oceanH;
        const waveAmp = 0.6 + normW * 4.0;
        const waveFreq = 0.028 - normW * 0.015;

        ctx.beginPath();
        for (let p = 0; p <= 80; p++) {
          const nx = p / 80;
          const x = nx * width;
          const waveOffset = Math.sin(x * waveFreq + t * (0.32 + normW * 0.45) + w * 1.2) * waveAmp;
          const y = lineY + waveOffset;
          if (p === 0) ctx.moveTo(x, y);
          else ctx.lineTo(x, y);
        }
        ctx.strokeStyle = `rgba(20, 52, 95, ${0.3 + normW * 0.3})`;
        ctx.lineWidth = 0.8 + normW * 1.2;
        ctx.stroke();

        const glitterCount = Math.floor(18 + normW * 26);
        const wakeCenterX = moonX + normW * (width * 0.20);
        const spreadWidth = (width * 0.24 + normW * width * 0.54) * oceanGlitter;

        for (let g = 0; g < glitterCount; g++) {
          const u = (Math.random() - 0.5) * 2;
          const gx = wakeCenterX + u * spreadWidth * (Math.random() * 0.7 + 0.3);
          const gy = lineY + (Math.random() - 0.5) * (waveAmp * 1.2);
          const dist = Math.abs(gx - wakeCenterX) / spreadWidth;
          const gaussianFalloff = Math.exp(-dist * dist * 1.3);

          const shimmer = Math.sin(t * 0.75 + g * 1.7 + normW * 4.2);
          if (shimmer > 0.15) {
            const alpha = Math.pow((shimmer - 0.15) / 0.85, 1.6) * gaussianFalloff * (0.42 - normW * 0.10);
            ctx.fillStyle = `rgba(215, 238, 255, ${alpha * 0.75})`;
            ctx.beginPath();
            ctx.arc(gx, gy, (0.7 + (1 - normW) * 1.3) * (shimmer * 0.5 + 0.5), 0, Math.PI * 2);
            ctx.fill();
          }
        }
      }
    }
  };
}

const defaultParams = [
  { key: "speed", label: "Night Sky Cadence", type: "range", min: 0.2, max: 2.5, step: 0.1, defaultValue: 1.0 },
  { key: "beamIntensity", label: "Volumetric Moonbeam Alpha", type: "range", min: 0.3, max: 2.2, step: 0.1, defaultValue: 1.2 },
  { key: "moonGlow", label: "Lunar Corona Dispersion", type: "range", min: 0.4, max: 2.0, step: 0.1, defaultValue: 1.1 },
  { key: "oceanGlitter", label: "Ocean Silver Reflection Path", type: "range", min: 0.4, max: 2.2, step: 0.1, defaultValue: 1.2 },
  { key: "bioluminescence", label: "Nocturnal Plankton Glow", type: "range", min: 0.2, max: 2.0, step: 0.1, defaultValue: 1.0 }
];

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

const instance = window.__art_instances['moonlit-ocean-rays'];
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
Imoon(θ)=I0eσrPMie(θ)O(θ,t),W(x,y)=e(xxmoon)22σwake2[ηL]mI_{\text{moon}}(\theta) = I_0 \cdot e^{-\sigma r} \cdot P_{\text{Mie}}(\theta) \cdot \mathcal{O}(\theta, t), \quad W(x, y) = e^{-\frac{(x - x_{\text{moon}})^2}{2\sigma_{\text{wake}}^2}} \cdot \left[\nabla \eta \cdot \mathbf{L}\right]^m
Click to expand
Compact Formula
I_lunar(r, θ) = I_0 exp(-σr) P(θ) · [Σ A_k cos(k θ + ωt)], wake = exp(-Δx²/2w²) · (N · L)^m

Mathematical Tags

#moonlight #night-sky #moonbeams #ocean-night #lunar-rays #bioluminescence #stars #optics
Author: Nocturnal Atmospheric Observatory Target: 60 FPS

Export & Embed: Moonlit Ocean & Lunar Rays

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/moonlit-ocean-rays" width="500" height="500" frameborder="0" loading="lazy"></iframe>
ESC
↑↓ Navigate Select
101 Mathematical Artworks

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! ✨