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.
60 FPS • Canvas 2D
Click + Drag to interact with field
</>
Full Executable Algorithm Code
220 lines
9121 chars
// 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(θ)=I0⋅e−σr⋅PMie(θ)⋅O(θ,t),W(x,y)=e−2σwake2(x−xmoon)2⋅[∇η⋅L]m
Click to expand
∑
Moonlit Ocean & Lunar Rays
Full Mathematical System • physics
100%
Complete System of Equations
[Governing Law][Discrete Progression]theta)=I0exp(−σr)P(theta)⋅[∑Akcos(ktheta+ωt)],wake=exp(−Δx2/2w2)⋅(N⋅L)m[Domain & Space][Parameter State]Imoon(θ)=I0⋅e−σr⋅PMie(θ)⋅O(θ,t),W(x,y)=e−2σwake2(x−xmoon)2⋅[∇η⋅L]mIlunar(r,x∈R2,t∈R+,ω∈[0,2π]λbeamIntensity=1.2(Moonbeam Radiance),λmoonGlow=1.3(Lunar Corona),λoceanGlitter=1.1(Silver Moon Wake),λbioluminescence=1(Bioluminescent Tide)
Imoon(θ)=I0⋅e−σr⋅PMie(θ)⋅O(θ,t),W(x,y)=e−2σwake2(x−xmoon)2⋅[∇η⋅L]m
Computational Implementation (JavaScript Engine Equivalent)
I_lunar(r, θ) = I_0 exp(-σr) P(θ) · [Σ A_k cos(k θ + ωt)], wake = exp(-Δx²/2w²) · (N · L)^m 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
Press ESC or F to exit