---
title: "Priority effects and alternative stable states"
description: "Priority effects in R: how strong mutual competition creates two alternative stable states, so the winner is set by arrival order, not competitive rank."
date: "2026-08-02 13:00"
categories: [ecology tutorial, R, community ecology, coexistence, theoretical ecology]
image: thumbnail.png
image-alt: "Two competition trajectories from mirror-image starts, one ending in each species winning, showing founder control."
---
```{r setup}
#| include: false
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE,
dev = "png", dpi = 120, fig.path = "figures/",
fig.width = 7, fig.height = 4.4)
library(ggplot2)
theme_te <- theme_minimal(base_size = 12) +
theme(panel.background = element_rect(fill = "white", colour = NA),
plot.background = element_rect(fill = "white", colour = NA),
panel.grid.minor = element_blank(),
plot.title = element_text(face = "bold", size = 12))
theme_set(theme_te)
col_1 <- "#3a6b52"; col_2 <- "#b5651d"; col_ref <- "#555555"
r1 <- r2 <- 0.5; K1 <- K2 <- 100; a12 <- a21 <- 1.5 # interspecific > intraspecific: founder control
lv <- function(N10, N20, Tn = 200, dt = 0.1) {
N1 <- N10; N2 <- N20; out <- data.frame(t = 0, N1 = N1, N2 = N2)
for (t in 1:(Tn/dt)) {
dN1 <- r1*N1*(1 - (N1 + a12*N2)/K1); dN2 <- r2*N2*(1 - (N2 + a21*N1)/K2)
N1 <- max(N1 + dN1*dt, 0); N2 <- max(N2 + dN2*dt, 0)
if (t %% 5 == 0) out <- rbind(out, data.frame(t = t*dt, N1 = N1, N2 = N2))
}
out
}
end_state <- function(a, b) { z <- lv(a, b); c(N1 = tail(z$N1, 1), N2 = tail(z$N2, 1)) }
Nstar <- K1/(1 + a12)
e_ab <- end_state(60, 40); e_ba <- end_state(40, 60)
```
Two species compete for the same space, one arrives a little ahead of the other, and it wins, permanently, even though the two are evenly matched. Run it again with the late arriver first and the outcome flips. Nothing about the species changed; only the order of arrival did. This is a priority effect, and when it is strong enough it means a community has more than one stable configuration it can settle into. Which one you get is a matter of history, not of who is the better competitor.
## When competition inverts the usual rule
The Lotka-Volterra competition model has a well-known set of outcomes, and the interesting one is the least discussed. Each species limits itself through its own density and limits its competitor through cross-competition. When a species limits *itself* more than it limits the other, coexistence follows; that is the stable, familiar case. But when each species limits the *other* more than itself, the model inverts: whichever species gets ahead suppresses its rival faster than it suppresses itself, so its lead compounds until the rival is gone. Set the cross-competition coefficients above one, so each species is its competitor's worst enemy, and integrate from two starting points that differ only in who leads.
```{r founder}
r1 <- r2 <- 0.5; K1 <- K2 <- 100; a12 <- a21 <- 1.5 # each hurts the other more than itself
step <- function(N, dt = 0.1) {
dN1 <- r1*N[1]*(1 - (N[1] + a12*N[2])/K1); dN2 <- r2*N[2]*(1 - (N[2] + a21*N[1])/K2)
pmax(N + c(dN1, dN2)*dt, 0)
}
run <- function(N) { for (t in 1:2000) N <- step(N); round(N) }
rbind(species1_ahead = run(c(60, 40)), species2_ahead = run(c(40, 60)))
```
The two runs start as mirror images, `r sprintf("%.0f", 60)` against `r sprintf("%.0f", 40)` and the reverse, and they end at opposite corners: the first at `r sprintf("(%.0f, %.0f)", e_ab["N1"], e_ab["N2"])`, species 1 alone, the second at `r sprintf("(%.0f, %.0f)", e_ba["N1"], e_ba["N2"])`, species 2 alone. Same species, same competitive strengths, opposite winners. The system has two stable states, monoculture of one or monoculture of the other, and the initial lead decides which basin the community falls into.
```{r fig-traj}
#| fig-cap: "Two competition runs that differ only in which species starts slightly ahead. Each ends with a different species holding the whole site: the outcome is set by initial conditions, not by the species."
#| fig-alt: "Two trajectory panels; on the left species 1 rises to carrying capacity and species 2 falls to zero, on the right the reverse, from mirror-image starts."
d <- rbind(cbind(lv(60, 40), case = "species 1 starts ahead"),
cbind(lv(40, 60), case = "species 2 starts ahead"))
dl <- reshape(d, varying = c("N1", "N2"), v.names = "N", timevar = "sp", direction = "long")
ggplot(dl, aes(t, N, colour = factor(sp))) +
geom_line(linewidth = 0.9) + facet_wrap(~case) +
scale_colour_manual(values = c("1" = col_1, "2" = col_2), labels = c("species 1", "species 2")) +
labs(x = "Time", y = "Abundance", colour = NULL,
title = "Who arrives first wins") +
theme(strip.text = element_text(size = 9))
```
## The geometry of two stable states
The reason there are two stable states is visible in the zero-growth isoclines, the lines in the abundance plane along which each species stops changing. Species 1 holds steady where its own density plus the pressure from species 2 fills the site; species 2 has its own such line. When cross-competition is strong these two lines cross, and the crossing point is an unstable equilibrium: a knife-edge the community can sit on only if perfectly balanced, and rolls off at the slightest nudge.
```{r fig-isocline}
#| fig-cap: "Zero-growth isoclines for the two species. They cross at an unstable interior point (the critical balance); the two stable states are the corners where one species holds the whole site. The dashed diagonal is the boundary between the two basins."
#| fig-alt: "A phase plane with two crossing isocline lines meeting at an unstable point, an open circle at the crossing, filled circles at the two axis corners, and a dashed separatrix line."
N1 <- seq(0, 100, 1)
iso <- rbind(data.frame(N1 = N1, N2 = (K1 - N1)/a12, sp = "species 1 isocline"),
data.frame(N1 = N1, N2 = K2 - a21*N1, sp = "species 2 isocline"))
iso <- iso[iso$N2 >= 0 & iso$N2 <= 100, ]
eq <- data.frame(N1 = c(100, 0, Nstar), N2 = c(0, 100, Nstar),
kind = c("stable", "stable", "unstable"))
ggplot(iso, aes(N1, N2, colour = sp)) +
geom_abline(slope = 1, intercept = 0, colour = col_ref, linetype = "dashed") +
geom_line(linewidth = 1) +
geom_point(data = eq, aes(N1, N2, shape = kind), colour = col_ref, size = 3, inherit.aes = FALSE) +
scale_shape_manual(values = c(stable = 16, unstable = 1)) +
scale_colour_manual(values = c("species 1 isocline" = col_1, "species 2 isocline" = col_2)) +
labs(x = "Species 1 abundance", y = "Species 2 abundance", colour = NULL, shape = NULL,
title = "Two stable corners, one unstable crossing")
```
The unstable crossing sits at `r sprintf("%.0f", Nstar)` of each species, and the two stable states are the corners. A community that starts on one side of the dividing line runs to one corner; start it on the other side and it runs to the other. That dividing line, the [basin boundary](../basins-of-attraction/), is what separates the two histories a community can have.
## Why this matters for real communities
Priority effects turn ecology from a story about which species is best into a story about what happened first, and that has consequences a competition ranking cannot capture. Restoration can fail or succeed on the order of planting; an invader that would lose to a resident at equal densities can hold ground once it is established first; the same patch of habitat, under identical conditions, can support different communities depending on its assembly history. The catch is that alternative stable states are genuinely hard to demonstrate in the field, because a community sitting in one state looks much like a community that simply has no alternative, and telling them apart takes the perturbation and hysteresis tests of the [checking post](../checking-for-alternative-stable-states/) rather than a snapshot. The mechanism is real and important; the evidence for it is where the care is needed.
## Related tutorials
- [Basins of attraction](../basins-of-attraction/)
- [Hysteresis and tipping points](../hysteresis-and-tipping-points/)
- [Checking for alternative stable states](../checking-for-alternative-stable-states/)
- [Annual plant competition models](../annual-plant-competition-models/)
## References
- Lewontin RC 1969. Brookhaven Symposia in Biology 22:13-24.
- Fukami T 2015. Annual Review of Ecology, Evolution, and Systematics 46:1-23 (10.1146/annurev-ecolsys-110411-160340).