What the simulation shows
Everyone starts susceptible except a handful of infected people. Infection moves people from S to I at a rate that depends on how many susceptible and infected people there are; recovery moves them from I to R at a constant per-person rate. Nobody enters or leaves the population, and recovered people stay immune.
- R₀ decides whether there is an epidemic at all. With β/γ below 1 each infected person causes fewer than one new infection and the curve of I only falls. Push β above γ and a wave appears.
- The peak happens when S reaches N/R₀. The dashed blue line marks that level. Once susceptibles drop below it, each case replaces itself less than once and infections decline — even though many people are still susceptible.
- Not everyone gets infected. The epidemic overshoots the threshold and then burns out with some people never infected. The lab compares the simulated total with the final-size equation below.
Try doubling the infectious period (halve γ) with β fixed, then halve β instead: both change R₀, but they change the timing differently.
The equations
With population N = S + I + R, transmission rate β and recovery rate γ (both per day):
The three right-hand sides sum to zero, so N stays constant; the lab reports the largest numerical drift from that as a check. The mean infectious period is 1/γ days.
Two quantities follow directly from the equations:
- Basic reproduction number R₀ = β/γ — new infections caused by one case in a fully susceptible population.
- Peak condition — dI/dt = 0 when βS/N = γ, that is when S = N/R₀.
Dividing dS/dt by dR/dt and integrating gives the final-size relation for an epidemic that starts with R(0) = 0 and ends with no infected people:
For R₀ = 3 the solution is about 94% ever infected, well above the 1 − 1/R₀ ≈ 67% herd-immunity threshold.
How the simulation solves it
The system has no closed-form solution for I(t), so the model steps it forward with the classical fourth-order Runge–Kutta method (RK4) at a fixed step of 0.05 days, recording the state once per day. RK4 evaluates the derivatives four times per step and combines them; its error per unit time shrinks with the fourth power of the step size. For these smooth curves a 0.05-day step is far more accurate than needed — halving it changes the reported peak by less than one person. Euler vs Runge–Kutta shows why the simpler Euler method is a poor choice here.
The peak is taken from the RK4 steps themselves, so it is resolved to 0.05 days.
Example result
With β = 0.3, γ = 0.1 (R₀ = 3.00) in a population of 100,000 starting from 10 cases, infections peak around day 49.9 at about 30,050 people. By day 180, 94.0% of the population has been infected, matching the final-size equation (94.0%). The sum S + I + R never drifts more than 2.0e-10 people from N.
| Basic reproduction number R₀ | 3.00 |
|---|---|
| Peak infected | 30,050 |
| Peak day | 49.9 |
| Ever infected by end | 94.0% |
| Final size (theory, t → ∞) | 94.0% |
| max |S + I + R − N| | 2.0e-10 |
Implementation
The page runs simulation.js from the package below; the Python file is a line-by-line port with the same random-number generator. The excerpts show the core of each.
// dS/dt = −βSI/N, dI/dt = βSI/N − γI, dR/dt = γI
export function derivatives(s, i, beta, gamma, n) {
const infection = (beta * s * i) / n;
const recovery = gamma * i;
return [-infection, infection - recovery, recovery];
}
// One classical Runge–Kutta (RK4) step of size h.
export function rk4Step(state, h, beta, gamma, n) {
const [s, i, r] = state;
const k1 = derivatives(s, i, beta, gamma, n);
const k2 = derivatives(s + (h / 2) * k1[0], i + (h / 2) * k1[1], beta, gamma, n);
const k3 = derivatives(s + (h / 2) * k2[0], i + (h / 2) * k2[1], beta, gamma, n);
const k4 = derivatives(s + h * k3[0], i + h * k3[1], beta, gamma, n);
return [
s + (h / 6) * (k1[0] + 2 * k2[0] + 2 * k3[0] + k4[0]),
i + (h / 6) * (k1[1] + 2 * k2[1] + 2 * k3[1] + k4[1]),
r + (h / 6) * (k1[2] + 2 * k2[2] + 2 * k3[2] + k4[2]),
];
}def derivatives(s, i, beta, gamma, n):
"""dS/dt = -beta*S*I/N, dI/dt = beta*S*I/N - gamma*I, dR/dt = gamma*I"""
infection = beta * s * i / n
recovery = gamma * i
return (-infection, infection - recovery, recovery)
def rk4_step(state, h, beta, gamma, n):
s, i, r = state
k1 = derivatives(s, i, beta, gamma, n)
k2 = derivatives(s + h / 2 * k1[0], i + h / 2 * k1[1], beta, gamma, n)
k3 = derivatives(s + h / 2 * k2[0], i + h / 2 * k2[1], beta, gamma, n)
k4 = derivatives(s + h * k3[0], i + h * k3[1], beta, gamma, n)
return (
s + h / 6 * (k1[0] + 2 * k2[0] + 2 * k3[0] + k4[0]),
i + h / 6 * (k1[1] + 2 * k2[1] + 2 * k3[1] + k4[1]),
r + h / 6 * (k1[2] + 2 * k2[2] + 2 * k3[2] + k4[2]),
)Run the full model from a terminal:
node simulation.js --population 100000 --initial-infected 10
python simulation.py --population 100000 --initial-infected 10Download the model
| File | Contents | Size |
|---|---|---|
README.md | How to run it, parameters, example result | 2.2 KB |
model.json | Parameters with ranges and defaults, outputs, the seed-42 example | 2.3 KB |
simulation.js | The model used on this page plus a command-line runner (Node 18+) | 7.9 KB |
simulation.py | Python 3.9+ port, standard library only | 6.7 KB |
example.csv | Result table from the example run | 10.2 KB |
LICENSE.txt | MIT License | 1.0 KB |
Assumptions & limitations
- Homogeneous mixing. Every infected person is equally likely to meet every susceptible one. Real contact is structured by households, age, schools and geography; an epidemic on a network shows what changes when it is not.
- Deterministic and continuous. The equations treat people as a continuous quantity and have no chance events, so an outbreak starting from 10 cases always takes off when R₀ > 1. In small populations, early chance extinction is common.
- Exponential infectious period. A constant recovery rate γ means infectious periods are exponentially distributed; real diseases have more regular durations and often a latent (exposed) stage, handled by SEIR-type models.
- Fixed parameters. β and γ do not change with behavior, seasons, interventions or vaccination. There are no births, deaths or waning immunity.
- An educational model. It captures the mechanism of an outbreak, not the course of any real disease. It is not a forecasting tool.
References
- Kermack, W. O. & McKendrick, A. G. (1927). A contribution to the mathematical theory of epidemics. Proceedings of the Royal Society A, 115(772), 700–721. doi.org/10.1098/rspa.1927.0118
- Keeling, M. J. & Rohani, P. (2008). Modeling Infectious Diseases in Humans and Animals, chapter 2. Princeton University Press.
- Hethcote, H. W. (2000). The mathematics of infectious diseases. SIAM Review, 42(4), 599–653. doi.org/10.1137/S0036144500371907