Left: prey (cyan) and predator (red) populations over time. Right: phase portrait, each closed orbit is a conserved trajectory. Click the phase canvas to set a new initial condition.
In 1918, fishing across the Adriatic Sea nearly stopped. Naval mines, wartime conscription, and blocked ports made commercial fishing too dangerous to bother. When catches resumed after the armistice, marine biologist Umberto D'Ancona noticed something unexpected in the port records: the proportion of predatory fish (sharks, skates, rays) had spiked during the war years and collapsed again once fishing resumed. In 1914, predators accounted for roughly 12% of catches at Fiume. By 1918, the number was 36%. By 1923, back down to 11%.
D'Ancona brought the data to his father-in-law, Vito Volterra. Volterra was a mathematician, not a biologist. He looked at the time series and wrote down two coupled differential equations. Let x be prey population and y be predators:
dx/dt = αx - βxy // prey grow, die when eaten
dy/dt = δxy - γy // predators grow by eating, die otherwise
The explanation Volterra produced matched D'Ancona's data directly. Fishing removed both species, but it hit predators proportionally harder, since predators occupy a smaller fraction of total biomass and their populations recover more slowly. Reducing fishing let prey rebound first, which then fed a predator surge. The model predicted both the wartime spike and the post-war decline. Volterra published in 1926; Alfred Lotka had derived the same equations independently in 1925 while studying autocatalytic chemical reactions. The convergence was clean enough that their names stuck together.
The qualitative behavior of the system lives in the phase portrait on the right panel. Each point in the plane represents a population state: how many prey, how many predators. From any starting point, the system traces a closed orbit around a fixed point at (γ/δ, α/β). The orbits never spiral inward or outward; the system is conservative, like a frictionless pendulum. Change the initial condition and you get a larger or smaller orbit, but always closed.
// RK4 integration step
const dx1 = alpha * x - beta * x * y;
const dy1 = delta * x * y - gamma * y;
const x2 = x + 0.5 * dt * dx1;
const y2 = y + 0.5 * dt * dy1;
const dx2 = alpha * x2 - beta * x2 * y2;
const dy2 = delta * x2 * y2 - gamma * y2;
// ... two more midpoint evaluations ...
x += dt * (dx1 + 2*dx2 + 2*dx3 + dx4) / 6;
y += dt * (dy1 + 2*dy2 + 2*dy3 + dy4) / 6;
The visualization runs fourth-order Runge-Kutta at dt = 0.005, taking up to 32 substeps per animation frame. Simple Euler integration at any reasonable step size drifts off the conserved orbit and either spirals in or out. RK4 preserves the closed-orbit structure well enough to show the correct qualitative behavior for thousands of steps. The phase canvas accepts click input to place a new starting condition, which traces a new orbit at a different radius from the equilibrium.
The Lotka-Volterra equations are the minimal system that produces sustained population oscillation without external forcing. They sit at the root of almost every ecological model written since 1926: competition models, food web cascades, epidemic compartmental models. Volterra himself proved three theorems from the equations: that populations oscillate indefinitely, that time-averaged populations equal the equilibrium values regardless of amplitude, and that proportional harvesting of all species shifts the equilibrium in favor of prey. That last result explained D'Ancona's wartime data exactly.
Lotka–Volterra
RK4 integration of the predator-prey system. Dual canvas: time series and phase portrait. Adjustable α, β, δ, γ and initial conditions. Click the phase portrait to set a new starting state.
View artifact →