terra is a super customizable framework for creating and analyzing biological simulations. It's open-source and licenced under MIT.

Usage

Including terra

Getting started is as easy as including the script!

<script src="path/to/terra.min.js"></script>

terra can also be used as a module with most popular module systems: ?

// CommonJS
var terra = require('./terra.min.js');
// ...

// AMD
define(['./terra.min.js'] , function (terra) {
    return function () { //... };
});

// ...and if you're not using a module system, it'll be in
window.terra;

If you manage dependencies with Bower, you're in luck!

bower install terra

Creating creatures

Let's create a simple creature using the creatureFactory. Each creature requires a type.

terra.creatureFactory.register({
  type: 'firstCreature'
});

This creature is valid, but it's pretty boring. To make a more interesting creature, let's override some of the default attributes and methods.

terra.creatureFactory.register({
  type: 'secondCreature',
  color: [120, 0, 240],
  sustainability: 6,
  reproduceLv: 1
});

We've just created a purple creature that only eats if 6 or more plants are around it. These creatures basically seek out an edge or corner and die there.

Creating the environment

To run a simulation, we'll need to create an environment. Let's make a 25x25 grid, populate 10% of the space with our lonely purple creature, and fill the rest with simple plants.

var ex1 = new terra.Terrarium(25, 25, 'ex1');
ex1.populate([['secondCreature', 10], ['simplePlant', 90]]);

Running a simulation

Terrariums have a few methods that allow you to interact with the simulation. Let's animate it and see how it does for the first 300 steps.

ex1.animate(300);

That's all there is to it! Though it's possible to generate complex behaviours by simply overriding default values, the real fun comes when you realize that creatures are entirely customizable.

Examples

Conway's Game of Life ?

var gameOfLife = new terra.Terrarium(25, 25);

terra.creatureFactory.register({
  type: 'GoL',
  colorFn: function () { return this.alive ? this.color + ',1' : '0,0,0,0'; },
  wait: function () {},
  isDead: function () { return false; },
  queue: function (neighbors) {
    var surrounding = _.filter(neighbors, function (spot) {
      return spot.creature.alive;
    }).length;
    this.alive = surrounding === 3 || surrounding === 2 && this.alive;
    return false;
  }
}, function () {
  this.alive = Math.random() < 0.5;
});

gameOfLife.populate([['GoL', 100]]);
gameOfLife.animate();

Cyclic Cellular Automaton ?

var cyclic = new terra.Terrarium(100, 100);

terra.creatureFactory.register({
  type: 'cyclic',
  colors: ['255,0,0,1', '255,96,0,1', '255,191,0,1', '223,255,0,1', '128,255,0,1', '32,255,0,1', '0,255,64,1', '0,255,159,1', '0,255,255,1', '0,159,255,1', '0,64,255,1', '32,0,255,1', '127,0,255,1', '223,0,255,1', '255,0,191,1', '255,0,96,1'],
  colorFn: function () { return this.colors[this.state];},
  wait: function () {},
  isDead: function () { return false; },
  queue: function (neighbors) {
    var next = (this.state + 1) % 16;
    var changing = _.some(neighbors, function (spot) {
      return spot.creature.state === next;
    });
    if (changing) this.state = next;
  }
}, function () {
  this.state = Math.floor(Math.random() * 16);
});

cyclic.populate([['cyclic', 100]]);
cyclic.animate();

Brutes and Bullies

// the demo running at the top of this page
var paTerrarium = new terra.Terrarium(25, 25);

terra.creatureFactory.register({
  type: 'plant',
  color: [0, 120, 0],
  size: 10,
  initialEnergy: 5,
  maxEnergy: 20,
  wait: function() {
    // photosynthesis :)
    this.energy += 1;
  },
  move: false,
  reproduceLv: 0.65
});

creatureFactory.register({
  type: 'brute',
  color: [0, 255, 255],
  maxEnergy: 50,
  initialEnergy: 10,
  size: 20
});

creatureFactory.register({
  type: 'bully',
  color: [241, 196, 15],
  initialEnergy: 20,
  reproduceLv: 0.6,
  sustainability: 3
});

paTerrarium.populate([['plant', 50], ['brute', 5], ['bully', 5]]);
paTerrarium.animate();

If you come up with a cool example, let me know! I'll add it to this list and credit you.

Creatures

Creatures are registered with

terra.creatureFactory.register(options, init)

The following methods and attributes can be passed in an object as the first argument:

Required

Optional

The second argument to terra.creatureFactory.register is the init function. This function is run within a creature's constructor and allows you to set different attributes for individual creatures. For example, in the Cyclic Cellular Automaton example above we see the following init function:

function () {
  this.state = Math.floor(Math.random() * 16);
});

Whenever a new creature is created of type 'cyclic', it will be randomly assigned a state of 0 to 15.

Terrarium

Terrariums are where the action happens. They're initialized with the following constructor:

var t = new terra.Terrarium(width, height, id, cellSize, insertAfter);

Required

Optional

Once initialized, terrariums have a few exposed methods. Using our terrarium t that we just created:

t.populate(creatures);
//populates the terrarium with a set distribution of creatures.
//<creatures> is an array of arrays of the form [string 'creatureName', int fillPercent]

t.grid = t.step(steps);
//t.step returns the next step of the simulation, or the grid after <steps> steps if specified.
//here, we're setting the terrarium's grid to the returned grid.

t.draw();
//updates the canvas to reflect the current grid

t.animate(steps, fn);
//starts animating the simulation. The simulation will stop after <steps> steps
//if specified, and call <fn> as a callback once the animation finishes.

t.stop();
//stops a currently running animation

Still want more? Check out the source on GitHub!