JSFiddle - React, Tailwind, and code Playground

HTML

<body>
    <script>
    console.clear();
    World.prototype.onTick = function() {
      this.out();
    }
    var world = new World({
      speed: -1,
      columns: 50,
      rows: 20,
      wrap: false,
      grid: [
        [0,0,0,0,0],
        [0,1,1,0,0],
        [0,0,1,1,0],
        [0,0,1,0,0],
        [0,0,0,0,0],
        [0,1,0,0,0],
        [0,1,1,0,0],
        [0,1,0,0,0],
        [0,0,0,0,0],
      ]
    });      
    
    world.tick();
    world.tick();
    world.tick();
    world.tick();
    world.tick();
    world.tick();
    world.tick();
    world.tick();
    world.tick();
    world.tick();
    world.tick();

  </script>
</body>

JavaScript

var utils = {};

/*
 * utils.extend()
 * - Extend initial object with all properties of following objects, objects later in the argument list take precedence.
 */
utils.extend = function(obj) {
  var args = Array.prototype.slice.call(arguments, 1);
  for (var i = args.length; i--;) {
    for (var prop in args[i]) {
      obj[prop] = args[i][prop];
    }
  }
  return obj;
}

/*
 * utils.defaults()
 * - Overwrite initial object with properties of following objects only if key is present in the initial object.
 */
utils.defaults = function(obj) {
  var args = Array.prototype.slice.call(arguments, 1);
  for (var i = args.length; i--;) {
    for (var prop in args[i]) {
      if (obj.hasOwnProperty(prop)) {
        obj[prop] = args[i][prop];
      }
    }
  }
  return obj;
}

/* no-wrap positioning functions */
var calcPos = {
  ul: function(cell) {
    return [cell.x - 1, cell.y - 1];
  },
  um: function(cell) {
    return [cell.x, cell.y - 1];
  },
  ur: function(cell) {
    return [cell.x + 1, cell.y - 1];
  },
  l: function(cell) {
    return [cell.x - 1, cell.y];
  },
  r: function(cell) {
    return [cell.x + 1, cell.y];
  },
  ll: function(cell) {
    return [cell.x - 1, cell.y + 1];
  },
  lm: function(cell) {
    return [cell.x, cell.y + 1];
  },
  lr: function(cell) {
    return [cell.x + 1, cell.y + 1];
  }
}

var worldDefaults = {
  rows: 50,
  columns: 50,
  wrap: true, // left edge is mirrored on right, top edge is mirrored on bottom. Vice versa
  speed: -1, // milliseconds (minimum time, waits until end of last tick to calculate from)
  grid: []
}
var World = function (opts) {
  this.settings = utils.defaults(worldDefaults, opts);
  
  this.maxX = this.settings.columns - 1;
  this.maxY = this.settings.rows -1;
  for (var y = 0, yLen = this.settings.rows; y < yLen; ++y) {
    for (var x = 0, xLen = this.settings.columns; x < xLen; ++x) { 
      if (y === 0) {
        this.cellList.push([]);
        if (this.settings.grid.length <= x) {
         ...