Chroma Field

to generate a new and unique variation

Source Code

Every variart piece is a small program. This is the one that drew what you're looking at.

// Vertical stripes, then triangles laid over them: inside a triangle the same
// stripes carry on but change colour, so the shape is read entirely in colour.
var backgroundColor = '#f2f0eb';
var colors = ['#ec3f8e', '#111111', '#2aa8c4', '#1c3fbf', '#f26722',
              '#8dc63f', '#f5c518', '#e8412c', '#6a4fbf', '#1f7a3f'];

var stripes = intVariable("Stripes", 9, 22);
var triangles = intVariable("Triangles", 1, 3);
var barWidth = intVariable("Bar Width", 35, 80);

var margin = 60;
var span = width - margin * 2;
var pitch = span / stripes;
var bar = pitch * (barWidth / 100);

s.rect(0, 0, width, height).attr({fill: backgroundColor});

// One run of bars, coloured by the given picker, as its own group.
function stripeRun(pick){
  var group = s.g();

  for (var i = 0; i < stripes; i++){
    var x = margin + i * pitch + (pitch - bar) / 2;
    group.add(s.rect(x, margin, bar, height - margin * 2).attr({fill: pick(i)}));
  }

  return group;
}

// Never the same colour as the bar to its left, so every edge reads.
function runningPicker(){
  var last = -1;

  return function(){
    var next = last;
    while (next == last){
      next = getRandomInt(0, colors.length - 1);
    }
    last = next;
    return colors[next];
  };
}

// A point somewhere along one side of the stripe field.
function edgePoint(side){
  var near = margin;
  var far = width - margin;

  if (side == 0) return [getRandomInt(near, far), near];
  if (side == 1) return [far, getRandomInt(near, far)];
  if (side == 2) return [getRandomInt(near, far), far];
  return [near, getRandomInt(near, far)];
}

function area(pts){
  return Math.abs((pts[2] - pts[0]) * (pts[5] - pts[1]) -
                  (pts[4] - pts[0]) * (pts[3] - pts[1])) / 2;
}

stripeRun(runningPicker());

// Each triangle takes a corner from three neighbouring sides of the field. Three
// corners can still crowd into one corner of it, so anything under a fifth of
// the field is thrown back: a sliver reads as a mistake rather than a shape.
for (var t = 0; t < triangles; t++){
  var smallest = span * span * 0.2;
  var points;

  for (var attempt = 0; attempt < 20; attempt++){
    var first = getRandomInt(0, 3);
    points = [];

    for (var p = 0; p < 3; p++){
      points = points.concat(edgePoint((first + p) % 4));
    }

    if (area(points) >= smallest) break;
  }

  var mask = s.g(s.polygon(points).attr({fill: '#ffffff'}));
  stripeRun(runningPicker()).attr({mask: mask});
}