📄 Traffic - README.txt
×

Traffic

PhysicsSocial Science CanvasRenderer

This model illustrates a counterintuitive phenomenon familiar to anyone who has experienced rush-hour traffic: jams that appear to arise from nothing. Based on car-following models in traffic flow theory, each agent (car) accelerates when the road ahead is clear and brakes when approaching another vehicle. These simple local rules produce emergent traffic waves—backward-propagating pulses of congestion.

When one agent brakes, the agent behind must brake harder (due to reaction time), and so on down the line—a cascade that amplifies small perturbations into stop-and-go waves. The phenomenon is a striking example of how global patterns can emerge without any global cause: no accident, no bottleneck, just the aggregate effect of many drivers following simple rules. Under certain density and parameter regimes, the system reaches a smooth flow; under others, it remains locked in perpetual oscillation.

💻 traffic.js - Interactive Editor
×
import { Agent, Environment, CanvasRenderer, utils, Histogram } from "flocc";
utils.seed(1);

/* ------- PARAMETERS --------- */

const NUM_CARS = 32;
const ACCELERATION = 1.1;
const MAX_SPEED = 18;
const MIN_SPEED = 0.5;
const width = window.innerWidth;
const height = 200;

/* ---------------------------- */

const environment = new Environment();
const container = document.getElementById("container");
const renderer = new CanvasRenderer(environment, {
  background: "#eeeeee",
  width,
  height
});
renderer.mount(container);

const histogram = new Histogram(environment, {
  buckets: 3,
  width,
  height,
  max: MAX_SPEED / 10
});
histogram.metric("vx");
histogram.mount("#histogram");

function setup() {
  for (var i = 0; i < NUM_CARS; i++) {
    const agent = new Agent({
      x: utils.uniform() * width,
      y: height / 2,
      vx: 0.5 + utils.uniform(),
      vy: 0,
      size: 2.5,
      shape: "arrow"
    });

    agent.addRule(tick);

    environment.addAgent(agent);
  }
}

function tick(agent) {
  let { x, vx } = agent.getData();

  let isAhead = environment.getAgents().some((neighbor) => {
    const nx = neighbor.get("x");
    // if within 5 of velocity
    if (Math.abs(nx - x) < 5 * vx) {
      // must be ahead, not behind
      if (nx > x || (x + 5 * vx > width && nx - 5 * vx < 0)) return true;
    }
    return false;
  });

  x += vx;
  while (x < 0) x += width;
  while (x >= width) x -= width;

  vx *= isAhead ? 0 : ACCELERATION;
  vx = utils.clamp(vx, Math.max(MIN_SPEED / 10, 0.01), MAX_SPEED / 10);

  return { x, vx };
}

function run() {
  environment.tick({ randomizeOrder: true });
  requestAnimationFrame(run);
}

setup();
run();

Edit the code on the left · See results on the right