Here is the genetic development of a cancer from start to finish. At the start, the tumour is very small, and similar to the host (the root of the tree is the host genotype). Over time, mutations occur and the cancer becomes more virulent, and grows.

Variation also occurs within the cancer, leading to subpopulations. Some of these die out, due to evolutionary dead-ends. In some simulations, all the cancer ends up in the backwater, and so remains benign.

This model is based on the cancer phylogeny reconstructed by Marco Gerlinger. Variation is more successful to the right: these are driver mutations. Vertical change gives no reproductive benefits; this change is passenger. Beneficial mutations produce a snowballing effect: as the tumour progresses it both grows and becomes more competitive. Variation is noisy so each model run behaves differently; over several runs we see all the diversity found within the tumour.

The reconstructed tree does not consider the order of mutations within a branch. How do passenger mutations fix? I modified my model to investigate. Here I considered that some mutation combinations might be unviable.

 
 

Code in Processing, its javascript variant p5

var landscape;
var space;

function preload() {
  landscape = loadImage("landscape.png");
}

function setup() {
  canvas = createCanvas(0.9 * windowWidth, 0.9 * windowHeight);
  space = new Space();
}

function draw() {
  space.grow();
  space.kill();
  space.draw();
  diagnostic();
}

function Space() {
  this.cells = [];

  for (var i = 0; i < 10; i++) {
    this.cells.push(new Cell(0.12 * width, 0.5 * height));
  }

  this.grow = function() {
    for (var i = this.cells.length - 1; i >= 0; i--) {
      this.cells[i].mature();
      if (this.cells[i].age > 10) {
        this.cells.push(new Cell(this.cells[i].x, this.cells[i].y));
        this.cells[i].age = 0;
      }
    }
  };

  this.kill = function() {
    var deathrate = log(this.cells.length);
    for (var i = this.cells.length - 1; i >= 0; i--) {
      if (!this.cells[i].in) {
        this.cells.splice(i, 1);
      }
    }
    for (i = this.cells.length - 1; i >= 0; i--) {
      if (deathrate > random(120)) {
        this.cells.splice(i, 1);
      }
    }
  };

  this.draw = function() {
    background(255);
    background(landscape);
    ellipseMode(RADIUS);
    noStroke();
    smooth();
    fill(90, 175, 225, 100);
    for (var i = this.cells.length - 1; i >= 0; i--) {
      this.cells[i].draw();
    }
  };
}

function Cell(parentX, parentY) {
  this.x = parentX + random(-1, 1) * width / 100;
  this.y = parentY + random(-1, 1) * height / 100;
  this.age = 0;

  this.in = false;
  var c = get(this.x, this.y);
  if (255 - red(c) + 255 - green(c) + 255 - blue(c) > 60) {
    this.in = true;
  }

  this.mature = function() {
    this.age += random(4) * this.x / width;
  };

  this.draw = function() {
    ellipse(this.x, this.y, 0.01 * width);
  };
}

function diagnostic() {
  textFont("Futura", 0.05 * width);
  textAlign(LEFT, CENTER);
  fill(0, 90, 155);

  var timeUnits = "years";
  if (round(frameCount / 30 / 12) === 1) timeUnits = "year";
  text(round(frameCount / 30 / 12) + " " + timeUnits, 0.05 * width, 0.8 * height); // each frame is a day
  text(round(pow(space.cells.length, 1.0 / 3)) / 100 + " " + "millimetres", 0.05 * width, 0.9 * height); //cell is 10μm wide
}