---
title: "Biham-Middleton-Levine traffic model"
date: "2014-12-25"
---


The [BML traffic model](https://en.wikipedia.org/wiki/Biham%E2%80%93Middleton%E2%80%93Levine_traffic_model) is a simple 3-state cellular automaton consisting of two kinds of cars, one facing right and one facing down, taking turns to move. Here is a demo which allows you to explore the model, as well as its variants as described in [my paper](http://arxiv.org/abs/1501.00733).

<style>
#holder {
    margin: 0 auto;
}
#bml {
    margin: 0 auto;
}
#rulec {
    margin-top: 1em;
    width: 100%;
}
input {
    width: 300px;
}
</style>
<div id="holder">
<canvas id="bml" width="300" height="300"></canvas>
</div>
<canvas id="rulec" width="810" height="24"></canvas>
<p>Framerate: <span id="fps">0</span></p>
<p>Rule: <input id="rule" type="text" onchange="updateRule(+this.value)"> <button onclick="randomRule()">random</button></p>
<p>Density: 0% <input id="density" type="range" value="0.36" min="0" max="1" step="0.01" onchange="updateDensity(+this.value)"> 100%</p>
<p>Simulation speed: 1 <input id="speed" type="range" value="1" min="1" max="10" step="1" onchange="updateSpeed(+this.value)"> 10</p>

The simulation is randomly initialized. The probability of each cell being non-empty is Density.

The bar underneath the main simulation is a visualization of the next state of a cell as a function of its two neighbours and itself.

| Name | Rulestring |
| --- | --- |
| BML traffic model | [3922832263383](#0.36/3922832263383) |
| Diagonal sand flow | [7469071910973](#0.36/7469071910973) |
| Blue earthworms | [5340268068864](#0.5/5340268068864) |
| Phase change around 62% | [2885464785241](#0.62/2885464785241) |
| Phase change around 75% | [3894972317834](#0.75/3894972317834) |
| Phase change around 75% | [651706795916](#0.745/651706795916) |
| Phase change around 58% | [2168120246479](#0.58/2168120246479) |
| Phase change around 66% | [152690720768](#0.665/152690720768) |
| Weird stuff | [2828173986213](#0.36/2828173986213) |
| Weird stuff | [486874543](#0.3/486874543) |
| Weird stuff | [1811177701721](#0.5/1811177701721) |
| Weird stuff | [3950779072581](#0.5/3950779072581) |
| Weird stuff | [3118088093765](#0.5/3118088093765) |
| Weird stuff | [400697024](#0.5/400697024) |
| Weird stuff | [3245024084244](#0.5/3245024084244) |
| Weird stuff | [6212768399705](#0.5/6212768399705) |
| Weird stuff | [273952194560](#0.5/273952194560) |
| Weird stuff | [1217877770240](#0.5/1217877770240) |
| Weird stuff | [456351711232](#0.5/456351711232) |
| Weird stuff | [503443685376](#0.5/503443685376) |
| Weird stuff | [189398122772](#0.5/189398122772) |
| Weird stuff | [1488785113364](#0.5/1488785113364) |
| Weird stuff | [7466274390292](#0.5/7466274390292) |
| Weird stuff | [3580146745344](#0.5/3580146745344) |
| Weird stuff | [2403954491737](#0.5/2403954491737) |
| Weird stuff | [72022532096](#0.5/72022532096) |
| Weird stuff | [4807504001841](#0.5/4807504001841) |
| Weird stuff | [5882493049933](#0.5/5882493049933) |

Table: List of interesting cellular automaton rules found.

<script>
    var RULE = [0,0,0,0,2,2,1,1,1,2,2,2,0,2,2,1,1,1,0,0,0,0,2,2,1,1,1];
    var rule_10 = 3922832263383;
    var STATE2RED = [255, 255, 0];
    var STATE2GREEN = [255, 60, 153];
    var STATE2BLUE = [255, 0, 255];
    var DENSITY = 0.36;
    var requestAnimFrame = window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        function(callback, element) { setTimeout(callback, 1000/60); };
    var lastCalledTime;
    var fps;
    var ind = 0;
    var vv = 1;
    var playing = 1;
    var iter = 0;
    var xsize, ysize, slexip, pixles, context, imageData, pixels;
    window.onload = function() {
        var canvas = document.getElementById("bml");
        ysize = canvas.height;
        xsize = canvas.width;
        slexip = [], pixles = [];
        window.onhashchange = gethash;
        gethash();
        context = canvas.getContext("2d");
        imageData = context.getImageData(0, 0, canvas.width, canvas.height);
        pixels = imageData.data;
        run();

        document.addEventListener("keydown", function keyDownTextField(e) {
            var keyCode = e.keyCode;
            if(keyCode === 39) {
                playing = false;
                step(1);
            } else if(keyCode === 27) {
                playing = false;
            }
        }, false);
    };
      function gethash() {
        var hash = window.location.hash.replace('#','').split('/');
        if(parseFloat(hash[0])<=1 && parseFloat(hash[0])>=0) {
          DENSITY = parseFloat(hash[0]);
          document.getElementById('density').value = DENSITY;
        }
        if(hash.length==2 && parseInt(hash[1]) > 0 && parseInt(hash[1]) < Math.pow(3,27)) {
          rule_10 = parseInt(hash[1]);
          document.getElementById('rule').value = rule_10;
          var rule_10_tmp = rule_10;
          for(var i=0; i<27; i++) {
            RULE[i] = rule_10_tmp%3;
            rule_10_tmp = Math.floor(rule_10_tmp/3);
          }
        }
        init();
      };
      function init() {
        for(var x = 0; x<xsize*ysize; x++){
          if(Math.random()<DENSITY){
            pixles[x] = Math.random()<0.5?1:2;
          } else {
            pixles[x] = 0;
          }
          slexip[x] = 0;
        }
        var rulecanvas = document.getElementById('rulec');
        var ctx = rulecanvas.getContext('2d');
        ctx.clearRect(0,0,810,24);
        ctx.beginPath();
        ctx.rect(0,0,810,24);
        ctx.fillStyle = '#333';
        ctx.fill();
        for(var i=0; i<27; i++) {
            var k = i;
            for(var j=0; j<3; j++) {
                ctx.beginPath();
                ctx.rect(3 + i*30 + j*8, 0, 8, 8);
                ctx.fillStyle = 'rgb(' + STATE2RED[k%3] + ',' + STATE2GREEN[k%3] + ',' + STATE2BLUE[k%3] + ')';
                ctx.fill();
                k = ~~(k/3);
            }
            ctx.beginPath();
            ctx.rect(3 + i*30 + 5, 10, 14, 14);
            ctx.fillStyle = 'rgb(' + STATE2RED[[0,2,1][RULE[i]]] + ',' + STATE2GREEN[[0,2,1][RULE[i]]] + ',' + STATE2BLUE[[0,2,1][RULE[i]]] + ')';
            ctx.fill();
        }
      };
      function draw() {
        for(x = 0; x<xsize; x++){
          for(y = 0; y<ysize; y++){
            pixels[4*(y*xsize+x)+0] = STATE2RED[pixles[y*xsize+x]];
            pixels[4*(y*xsize+x)+1] = STATE2GREEN[pixles[y*xsize+x]];
            pixels[4*(y*xsize+x)+2] = STATE2BLUE[pixles[y*xsize+x]];
            pixels[4*(y*xsize+x)+3] = 255;
          }
        }
        context.putImageData(imageData, 0, 0);
      };
      function step(amount) {
        var x, y;
        for(var i = 0; i<amount; i++){
          for(x = 0; x<xsize; x++){
            for(y = 0; y<ysize; y++){
              slexip[y*xsize+x] = RULE[pixles[y*xsize+(x+1)%xsize]+3*pixles[y*xsize+x]+9*pixles[y*xsize+(x+xsize-1)%xsize]];
            }
          }
          for(x = 0; x<xsize; x++){
            for(y = 0; y<ysize; y++){
              pixles[y*xsize+x] = RULE[slexip[x+((y+1)%ysize)*xsize]+3*slexip[y*xsize+x]+9*slexip[x+((y+ysize-1)%ysize)*xsize]];
            }
          }
        }
        draw();
      };
      function run() {
        draw();
        if(playing) step(vv);
        if(ind++ % 10 == 0) {
            if(!lastCalledTime) {
                lastCalledTime = Date.now();
                fps = 0;
            } else {
                delta = (new Date().getTime() - lastCalledTime)/1000;
                lastCalledTime = Date.now();
                fps = 10/delta;
                document.getElementById('fps').innerHTML = ~~fps + ' fps';
            }
        }
        requestAnimFrame(run);
      };
    function updateDensity(density) {
      window.location.hash = '#' + density + '/' + rule_10;
    };
    function updateRule(rule) {
      window.location.hash = '#' + DENSITY + '/' + parseInt(rule);
    };
    function updateSpeed(speed) {
      vv = speed;
    }
    function randomRule() {
      rule_10 = Math.floor(Math.random()*Math.pow(3,27)+Math.random()*Math.pow(2,32)) % Math.pow(3,27);
      updateRule(rule_10);
    }
</script>
