OS-024 · Agents & cellular automata

Schelling segregation model

Two groups of agents live on a grid. Each agent is content if at least some share of its neighbors belong to its own group, and discontent agents move. Even when nobody minds being in the minority, the grid can end up far more segregated than anyone asked for.

Interactive model

OS-024 · schelling-segregation · seeded
group Agroup Bemptypale = discontent
like neighborsdiscontent agentsstart
Parameters
An agent is content if at least this share of its occupied neighbor cells hold its own group
Share of cells left vacant
Share of agents in group A (blue); the rest are group B (orange)
Maximum rounds; the run stops early once nobody moves
Same seed, same run — here, in simulation.js and in simulation.py.
Like neighbors, start
50.0%
Like neighbors, end
73.4%
Discontent agents, start
17.0%
Discontent agents, end
0.0%
Total moves
920
Rounds run
17
Nobody moved in round
17

Example run: default parameters, seed 42.

Mild preferences, strong sorting

Thomas Schelling's point in 1971 was about the gap between what individuals want and what the group gets. In the default run every agent is content as long as at least 30% of its neighbors are like itself: each would happily live as a minority, a preference compatible with a thoroughly mixed grid. The random starting grid is already close to that: most agents are content from the start. Yet the few discontent agents move, their departure and arrival tip other agents' neighborhoods over the threshold, and the process cascades until the grid is made of large single-color patches.

  • Watch the pale cells. They mark agents that are discontent right now. They are scattered at first, then concentrate along the borders between patches until none remain.
  • Compare the two numbers. The threshold is what each agent asks for; the like-neighbor share is what the population ends up with. The second is typically well above the first.
  • Sweep the threshold. Very low thresholds change almost nothing. Moderate ones produce the striking sorting. Near the top of the range almost nobody can be content, agents keep moving every round, and the grid never settles — high demands produce churn, not tidy segregation.

The exact update rule

The grid is L × L with edges that wrap around. Each cell is empty or holds one agent of group A or B. An agent's neighbors are the eight surrounding cells (the Moore neighborhood); empty cells do not count. With s like neighbors among o occupied neighbor cells and threshold θ, the agent is content when

s ≥ θ · o   or   o = 0
  1. Place the agents at random: shuffle all cells, fill the first ones with group A, the next with group B, and leave the rest empty.
  2. Each round, visit every agent exactly once in a new random order. This is a sequential (asynchronous) update: each agent is judged against the grid as it is at the moment of its visit, including moves made earlier in the same round.
  3. A discontent agent moves to an empty cell chosen uniformly at random — not necessarily one where it will be content — and its old cell becomes empty.
  4. Stop when a whole round passes with no moves (every agent is then content and nothing can change) or when the round limit is reached.

The segregation measure is the average, over agents with at least one neighbor, of the share of their neighbors that are like them. Under random placement it is close to a² + b², where a and b are the group shares — one half for equal groups.

Schelling's 1971 paper moved a discontent agent to the nearest location where it would be content. Random relocation, used here and in many teaching versions, reaches different numbers but shows the same mechanism. Synchronous updating, where all agents decide at once, is another variant with different dynamics.

What the model does and does not show

The model is an existence proof for a mechanism: strong aggregate segregation does not require strong individual preferences, or any central plan. It does not show that preferences are the main cause of segregation in any real city. Residential segregation has well-documented causes the model leaves out entirely — discrimination in housing, lending and employment, zoning and public housing policy, highways and school boundaries, and differences in income and wealth that restrict where people can afford to live. A run of this model says nothing about how much each of those contributes.

It also teaches a modeling lesson that carries over to flocking and cellular automata: local rules plus interaction produce patterns that no single agent's rule describes, so the aggregate outcome has to be simulated rather than read off the rule.

Example result

With the defaults — a 50 × 50 grid, 10% empty, equal groups, and agents content with at least 30% like neighbors — the random start has 50.0% like neighbors on average and 17.0% of agents discontent. After 920 moves nobody wanted to move in round 17, and the average share of like neighbors had risen to 73.4%.

Output of schelling-segregation with threshold=0.3 size=50 empty=0.1 share_a=0.5 rounds=100 seed=42
Like neighbors, start50.0%
Like neighbors, end73.4%
Discontent agents, start17.0%
Discontent agents, end0.0%
Total moves920
Rounds run17
Nobody moved in round17

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, so a given seed produces the same result in both languages. The excerpts show the core of each.

JavaScript — simulation.js
// An agent is content when at least a share θ of its occupied neighbor cells
// hold its own group (an agent with no neighbors is content).
export function content(st, c, threshold) {
  const [like, occupied] = neighbors(st, c);
  return occupied === 0 || like >= threshold * occupied;
}

// One round: visit every agent once, in a fresh random order. A discontent agent
// (judged at the moment it is visited) moves to an empty cell chosen uniformly at
// random; the cell it leaves becomes empty. Returns the number of moves.
export function round(st, threshold, rng) {
  const order = shuffle(Array.from({ length: st.pos.length }, (_, i) => i), rng);
  let moves = 0;
  for (const id of order) {
    const from = st.pos[id];
    if (content(st, from, threshold)) continue;
    const k = rng.int(st.empties.length);
    const to = st.empties[k];
    st.grid[to] = st.grid[from];
    st.grid[from] = 0;
    st.empties[k] = from;
    st.pos[id] = to;
    moves++;
  }
  st.round++;
  st.moves += moves;
  if (moves === 0 && st.settled === null) st.settled = st.round;
  return moves;
}
Python — simulation.py
def content(st, c, threshold):
    """Content if at least a share `threshold` of occupied neighbor cells hold the same group."""
    like, occupied = neighbors(st, c)
    return occupied == 0 or like >= threshold * occupied


def play_round(st, threshold, rng):
    """Visit agents once in random order; a discontent agent moves to a random empty cell."""
    order = shuffle(list(range(len(st["pos"]))), rng)
    grid, pos, empties = st["grid"], st["pos"], st["empties"]
    moves = 0
    for i in order:
        frm = pos[i]
        if content(st, frm, threshold):
            continue
        k = rng.int(len(empties))
        to = empties[k]
        grid[to], grid[frm] = grid[frm], EMPTY
        empties[k] = frm
        pos[i] = to
        moves += 1
    st["round"] += 1
    st["moves"] += moves
    if moves == 0 and st["settled"] is None:
        st["settled"] = st["round"]
    return moves

Run the full model from a terminal:

Shell
node simulation.js --threshold 0.3 --size 50 --seed 42
python simulation.py --threshold 0.3 --size 50 --seed 42

Download the model

schelling-segregation.zip6 files · 10.4 KBDownload .zip
Files in schelling-segregation.zip
FileContentsSize
README.mdHow to run it, parameters, example result2.5 KB
model.jsonParameters with ranges and defaults, outputs, the seed-42 example2.6 KB
simulation.jsThe model used on this page plus a command-line runner (Node 18+)9.4 KB
simulation.pyPython 3.9+ port, standard library only8.1 KB
example.csvResult table from the example run783 B
LICENSE.txtMIT License1.0 KB

Assumptions & limitations

  • Two groups, one trait, one preference. All agents share the same threshold and care only about the group of their immediate neighbors — not prices, schools, jobs or distance.
  • Moving is free and always possible. Any empty cell is available to anyone; there is no housing market, no cost and no discrimination by others.
  • Random relocation. Movers pick a random empty cell rather than searching for an acceptable one; the sequential random order is one of several update schemes, and each changes the numbers.
  • Torus grid. Edges wrap around, so no agent sits on a border.
  • A mechanism, not an explanation. The model shows that mild preferences can be sufficient for segregation; it cannot tell how much of real segregation they cause.

References

  1. Schelling, T. C. (1971). Dynamic models of segregation. Journal of Mathematical Sociology, 1(2), 143–186. doi.org/10.1080/0022250X.1971.9989794
  2. Wilensky, U. & Rand, W. (2015). An Introduction to Agent-Based Modeling. MIT Press.