In this model we plot cells in phenotype space: from left to right cells could be narrow to wide, from bottom to top they could be short to tall... or some other measurable properties. As cancer cells divide, phenotype varies a little. Over several generations, the colony spreads out in phenotype space.

Phenotype defines the physical properties of a cell – we are interested in cells resistance to our treatment drug of choice. Cells in the pale green region of phenotype space are sensitive to the drug: it kills them. Cells in the red region are resistant to the drug, and continue to flourish under treatment.

 
 

Code in Processing, its javascript variant p5

var landscape;
var space;
var EXPLAIN = false;
var DETECTED = false;

var WILD = 0;
var SENSITIVE = 1;
var RESISTANT = 2;

var typeColor = [];

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

function setup() {
  canvas = createCanvas(0.9 * windowWidth, 0.9 * windowHeight);

  typeColor[WILD] = color(190, 195, 195, 100);
  typeColor[RESISTANT] = color(0, 90, 155, 100);
  typeColor[SENSITIVE] = color(90, 175, 225, 100);

  space = new Space();
  frameRate(10);
}

function draw() {
  background(landscape);

  space.grow();
  space.kill();
  if (!DETECTED) {
    space.test();
  } else {
    space.treat();
  }

  space.draw();
  explain();
}

function touchEnded() {
  EXPLAIN = !EXPLAIN;
}

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

  var wildCentreX = random(0.4, 0.7) * width
  var wildCentreY = random(0.3, 0.7) * height;
  for (var i = 1; i < 30; i++) {
    this.cells.push(new Cell(wildCentreX, wildCentreY, random(this.Mature)));
  }

  this.test = function() {
    if (this.cells.length > 2000) DETECTED = true;
  };

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

  this.kill = function() {
    for (var i = this.cells.length - 1; i >= 0; i--) {
      if (random(2) > 1) {
        this.cells[i].mature();
        if (WILD === this.cells[i].type || random() < 0.1) {
          this.cells.splice(i, 1);
        }
      }
    }
  };

  this.treat = function() {
    for (var i = this.cells.length - 1; i >= 0; i--) {
      if (SENSITIVE === this.cells[i].type) {
        if (random(2) > 1) {
          this.cells.splice(i, 1);
        }
      }
    }
  };

  this.draw = function() {
    noStroke();
    for (var i = this.cells.length - 1; i >= 0; i--) {
      this.cells[i].draw();
    }
  };
}

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

  this.type = WILD;
  var c = get(this.x, this.y);
  if (green(c) < 170 && blue(c) < 170) {
    this.type = RESISTANT; // not green is red?
  } else if (red(c) < 230 && green(c) < 230 && blue(c) < 230) {
    this.type = SENSITIVE; // not red is green?
  }

  this.mature = function() {
    this.age += random();
  };

  this.draw = function() {
    fill(typeColor[this.type]);
    ellipse(this.x, this.y, width / 50, width / 50);
  };
}

function explain() {
  if (EXPLAIN) {
    rectMode(CENTER);
    textAlign(CENTER, CENTER);
    textFont("Futura", 0.05 * width);

    fill(255, 100);
    rect(0.2 * width, 0.25 * height, 0.4 * width, 0.5 * height);
    fill(100);
    if (!DETECTED) {
      text("the tumour is too small to be detected at the moment", 0.2 * width, 0.25 * height, 0.4 * width, 0.5 * height);
    } else {
      text("under treatment the viable landscape is reduced", 0.2 * width, 0.25 * height, 0.4 * width, 0.5 * height);
    }

    fill(255, 100);
    rect(0.8 * width, 0.3 * height, 0.3 * width, 0.07 * width);
    fill(90, 175, 225, 100);
    text("cancer cells", 0.8 * width, 0.3 * height);

    fill(255, 100);
    rect(0.75 * width, 0.55 * height, 0.45 * width, 0.07 * width);
    fill(landscape.get(int(0.6 * landscape.width), int(0.5 * landscape.height)));
    text("resistant landscape", 0.75 * width, 0.55 * height);

    fill(255, 100);
    rect(0.5 * width, 0.75 * height, 0.45 * width, 0.07 * width);
    fill(landscape.get(0.6 * landscape.width, 0.3 * landscape.height));
    text("sensitive landscape", 0.5 * width, 0.75 * height);

    fill(100);
    text("phenotype space", 0.2 * width, 0.9 * height);
  }
}