Mapping the Massive Migration
We are watching hundreds of thousands of salmon push up the Kenai River. But drawing each individual fish on a map would crash your phone instantly.
Instead, we use a Density Field (like a weather radar map). We group fish into "pulses" based on the day they entered the river from the ocean. As they swim upstream, they naturally spread out.
Waze for Salmon
Notice the floating tooltip markers? Just like Waze tracks traffic jams, our engine tracks the "Center of Mass" for each day's run. If 50,000 fish entered the river on July 15th, the marker stays precisely in the middle of that group as they migrate over 50+ miles to Skilak Lake.
Live River Conditions
Fish don't swim at a constant speed. We connect directly to the USGS Live Water API. When the river is running fast and deep with snowmelt, the simulated fish naturally slow down to fight the current.
1. Advection-Diffusion Equation
We computationally model the salmon migration using a localized 1D Advection-Diffusion partial differential equation for the density field ρ(x, t):
∂ρ / ∂t = -v(∂ρ / ∂x) + D(∂²ρ / ∂x²)
Where v is the advective drift velocity (tied directly to live USGS CFS flow data) and D is the biological diffusion coefficient (how much the cohort naturally spreads out over time).
2. Catmull-Rom Path Interpolation
The river path consists of discrete geographic points Pi from the true Alaska AWC database. To calculate the exact smoothly interpolated spatial position P(t) at any distance along the spline computationally efficiently, we employ a completely native Catmull-Rom formula:
P(t) = ½ [ (2P₁) + (-P₀ + P₂)t + (2P₀ - 5P₁ + 4P₂ - P₃)t² + (-P₀ + 3P₁ - 3P₂ + P₃)t³ ]
Because mathematical splines naturally "cut corners" on sharp geographic bends, we pre-process the AWC line with an O(n) polyline densification pass, forcing strict spatial enforcement sub-nodes every 15 meters.
3. Zero-Garbage WebGL Rendering
Every frame, the core 60fps loop binds a completely pre-allocated zero-garbage Float32Array directly to the graphics hardware. A fragment shader evaluates a generic probability distribution (Gaussian) for each spatial particle:
f(x) = a * exp( - (x - b)² / (2c²) )
Overlapping distributions are natively accumulated using the hardware gl.blendFunc(gl.ONE, gl.ONE) compositor state, completely bypassing JavaScript Main Thread bottlenecks.