Underwater Oceanic Sunbeams
Underwater light simulation illustrating sunlight penetrating deep oceanic waters. Computes Snell's law refraction through dynamic surface waves, creating volumetric cathedral light shafts, surface caustics, and illuminated schooling fish silhouettes.
60 FPS • Canvas 2D
Click + Drag to interact with field
</>
Full Executable Algorithm Code
202 lines
8152 chars
// 080 - Underwater Oceanic Sunbeams (physics)
// 1:1 Original algorithm engine source
function createUnderwaterOceanicSunbeams() {
const MARINE_SNOW_COUNT = 90;
const FISH_COUNT = 45;
const marineSnow = [];
const fishSchool = [];
function initOcean() {
marineSnow.length = 0;
for (let i = 0; i < MARINE_SNOW_COUNT; i++) {
marineSnow.push({
x: Math.random(),
y: Math.random(),
vx: (Math.random() - 0.5) * 0.0006,
vy: 0.0003 + Math.random() * 0.0007,
size: 0.8 + Math.random() * 2.0,
phase: Math.random() * Math.PI * 2,
});
}
fishSchool.length = 0;
for (let i = 0; i < FISH_COUNT; i++) {
const randType = Math.random();
let fSize;
let isLarge = false;
if (randType < 0.18) {
fSize = 16.0 + Math.random() * 10.0;
isLarge = true;
} else if (randType < 0.5) {
fSize = 8.5 + Math.random() * 6.5;
} else {
fSize = 3.8 + Math.random() * 4.2;
}
fishSchool.push({
x: Math.random(),
y: 0.32 + Math.random() * 0.54,
speed: (0.0006 + Math.random() * 0.0012) * (isLarge ? 0.75 : 1.1),
size: fSize,
phase: Math.random() * Math.PI * 2,
depth: Math.random(),
isLarge,
});
}
}
return {
setup() {
initOcean();
},
render(context, timeState, params) {
const { ctx, width, height } = context;
const speed = Number(params.speed ?? 1.0);
const shaftIntensity = Number(params.shaftIntensity ?? 1.2);
const waterClarity = Number(params.waterClarity ?? 1.0);
const causticSpeed = Number(params.causticSpeed ?? 1.3);
const kelpHeight = Number(params.kelpHeight ?? 1.0);
const t = timeState.time * speed;
if (marineSnow.length === 0) initOcean();
// Deep Ocean Water
const waterGrad = ctx.createLinearGradient(0, 0, 0, height);
waterGrad.addColorStop(0, '#044368');
waterGrad.addColorStop(0.25, '#022949');
waterGrad.addColorStop(0.6, '#01162d');
waterGrad.addColorStop(1.0, '#000814');
ctx.fillStyle = waterGrad;
ctx.fillRect(0, 0, width, height);
const lightX = width * 0.5 + Math.sin(t * 0.3) * (width * 0.04);
const lightY = height * 0.08;
// 2. Surface Caustics & Water Ripple Band (Soft, Gentle Refraction)
ctx.save();
ctx.globalCompositeOperation = 'screen';
const CAUSTIC_CURVES = 14;
for (let c = 0; c < CAUSTIC_CURVES; c++) {
const normC = c / CAUSTIC_CURVES;
const cy0 = normC * (height * 0.10);
ctx.beginPath();
const steps = 60;
for (let s = 0; s <= steps; s++) {
const nx = s / steps;
const x = nx * width;
const wave1 = Math.sin(nx * 14 + t * causticSpeed * 0.7 + c) * 5;
const wave2 = Math.cos(nx * 24 - t * causticSpeed * 0.5) * 3;
const y = cy0 + wave1 + wave2;
if (s === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
const causticAlpha = (1 - normC) * 0.18 * waterClarity;
ctx.strokeStyle = `rgba(160, 230, 255, ${causticAlpha})`;
ctx.lineWidth = 1.0 + (1 - normC) * 1.5;
ctx.stroke();
}
ctx.restore();
// Volumetric Sun Shafts
ctx.save();
ctx.globalCompositeOperation = 'screen';
const SHAFT_COUNT = 38;
const maxShaftLen = height * 1.25;
for (let i = 0; i < SHAFT_COUNT; i++) {
const normI = i / SHAFT_COUNT;
const baseAngle = Math.PI * 0.18 + normI * (Math.PI * 0.64);
const waveWarp = Math.sin(baseAngle * 7 + t * 0.8) * 0.05 + Math.cos(baseAngle * 13 - t * 1.1) * 0.03;
const shaftAngle = baseAngle + waveWarp;
const distFromCenter = Math.abs(normI - 0.5) * 2;
const beamIntensity = Math.pow(Math.max(0, 1 - distFromCenter * 0.8), 1.8);
const pulse = 0.65 + 0.35 * Math.sin(i * 2.3 + t * 1.8);
const alpha = Math.min(0.65, 0.32 * beamIntensity * pulse * shaftIntensity);
if (alpha > 0.02) {
const beamWidth = 0.035 + (1 - beamIntensity) * 0.02;
const shaftGrad = ctx.createRadialGradient(lightX, lightY, 0, lightX, lightY, maxShaftLen);
shaftGrad.addColorStop(0, `rgba(255, 255, 255, ${alpha * 1.4})`);
shaftGrad.addColorStop(0.12, `rgba(190, 245, 255, ${alpha})`);
shaftGrad.addColorStop(0.45, `rgba(40, 180, 230, ${alpha * 0.45})`);
shaftGrad.addColorStop(0.85, `rgba(10, 80, 150, ${alpha * 0.15})`);
shaftGrad.addColorStop(1.0, 'rgba(0, 30, 80, 0)');
ctx.beginPath();
ctx.moveTo(lightX, lightY);
ctx.lineTo(lightX + Math.cos(shaftAngle - beamWidth) * maxShaftLen, lightY + Math.sin(shaftAngle - beamWidth) * maxShaftLen);
ctx.lineTo(lightX + Math.cos(shaftAngle + beamWidth) * maxShaftLen, lightY + Math.sin(shaftAngle + beamWidth) * maxShaftLen);
ctx.closePath();
ctx.fillStyle = shaftGrad;
ctx.fill();
}
}
ctx.restore();
// Core Glare
ctx.save();
ctx.globalCompositeOperation = 'screen';
const sunCoreGrad = ctx.createRadialGradient(lightX, lightY, 0, lightX, lightY, width * 0.4);
sunCoreGrad.addColorStop(0, 'rgba(255, 255, 255, 1.0)');
sunCoreGrad.addColorStop(0.08, 'rgba(220, 250, 255, 0.9)');
sunCoreGrad.addColorStop(0.28, 'rgba(80, 210, 255, 0.45)');
sunCoreGrad.addColorStop(1.0, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = sunCoreGrad;
ctx.beginPath();
ctx.arc(lightX, lightY, width * 0.4, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// Fish School
for (let f = 0; f < fishSchool.length; f++) {
const fish = fishSchool[f];
fish.x = (fish.x + fish.speed + 1) % 1;
const fx = fish.x * width;
const fy = fish.y * height + Math.sin(t * 2.2 + fish.phase) * (fish.isLarge ? 8 : 4);
const distFromLight = Math.abs(fx - lightX) / (width * 0.45);
const inBeamFactor = Math.max(0, 1 - distFromLight);
const hue = inBeamFactor > 0.35 ? 42 + (1 - inBeamFactor) * 55 : 188;
ctx.save();
ctx.fillStyle = `hsla(${hue}, 95%, ${inBeamFactor > 0.35 ? 65 : 35}%, 0.85)`;
ctx.beginPath();
ctx.ellipse(fx, fy, fish.size, fish.size * 0.42, 0, 0, Math.PI * 2);
ctx.fill();
const tailWag = Math.sin(t * (fish.isLarge ? 9 : 15) + fish.phase) * (fish.size * 0.35);
ctx.beginPath();
ctx.moveTo(fx - fish.size * 0.75, fy);
ctx.lineTo(fx - fish.size * 1.6, fy - fish.size * 0.45 + tailWag);
ctx.lineTo(fx - fish.size * 1.6, fy + fish.size * 0.45 + tailWag);
ctx.closePath();
ctx.fill();
ctx.restore();
}
}
};
}
const defaultParams = [
{ key: "speed", label: "Ocean Current Cadence", type: "range", min: 0.2, max: 2.5, step: 0.1, defaultValue: 1.0 },
{ key: "shaftIntensity", label: "Cathedral Sunbeam Intensity", type: "range", min: 0.4, max: 2.2, step: 0.1, defaultValue: 1.2 },
{ key: "waterClarity", label: "Water Optical Clarity", type: "range", min: 0.3, max: 2.0, step: 0.1, defaultValue: 1.0 },
{ key: "causticSpeed", label: "Surface Caustic Refraction", type: "range", min: 0.4, max: 2.5, step: 0.1, defaultValue: 1.3 },
{ key: "kelpHeight", label: "Kelp Canopy Elevation", type: "range", min: 0.4, max: 1.6, step: 0.1, defaultValue: 1.0 }
];
if (!window.__art_instances) window.__art_instances = {};
if (!window.__art_instances['underwater-oceanic-sunbeams']) {
const inst = typeof createUnderwaterOceanicSunbeams === 'function' ? createUnderwaterOceanicSunbeams() : null;
if (inst && inst.setup) {
inst.setup({ ctx, width, height, dpr: 1, aspectRatio: width / height }, defaultParams);
}
window.__art_instances['underwater-oceanic-sunbeams'] = inst;
}
const instance = window.__art_instances['underwater-oceanic-sunbeams'];
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
n1sinθ1=n2sinθ2,I(z)=I0e−cz,C(x,y,t)=∣det(Jrefract(x,y,t))∣−1
Click to expand
∑
Underwater Oceanic Sunbeams
Full Mathematical System • physics
100%
Complete System of Equations
[Governing Law][Discrete Progression]thetai)=nwatersin(thetat),I(d)=I0exp(−αd)⋅wavecaustics(x,t)[Domain & Space][Parameter State]n1sinθ1=n2sinθ2,I(z)=I0e−cz,C(x,y,t)=∣det(Jrefract(x,y,t))∣−1nairsin(x∈R2,t∈R+,ω∈[0,2π]λshaftIntensity=1.2(Shaft Radiance),λwaterClarity=1(Water Clarity),λcausticSpeed=1.3(Caustic Motion),λkelpHeight=1(Kelp Canopy)
n1sinθ1=n2sinθ2,I(z)=I0e−cz,C(x,y,t)=∣det(Jrefract(x,y,t))∣−1
Computational Implementation (JavaScript Engine Equivalent)
n_air sin(θ_i) = n_water sin(θ_t), I(d) = I_0 exp(-αd) · wave_caustics(x, t) Compact Formula
n_air sin(θ_i) = n_water sin(θ_t), I(d) = I_0 exp(-αd) · wave_caustics(x, t) Mathematical Tags
#underwater
#sunbeams
#caustics
#snells-law
#ocean
#refraction
#god-rays
#marine-optics
Author: Marine Hydrodynamics Lab Target: 60 FPS
Press ESC or F to exit