Vibacious

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.

var bundles = intVariable('bundles', 1, 3);
var lineCount = intVariable('lines', 8, 16);
var dotSpacing = intVariable('dotSpacing', 12, 28);

var gold = '#EFB733';
var magenta = '#C12A7C';
var purple = '#5E3582';
var shade = '#6B2C74';
var silver = '#A7A2A8';
var margin = 60;
// Enough overdraw that the rotated ground still covers every corner
var bleed = 110;
var background = s.group();

background.add(s.rect(-bleed, -bleed, width + bleed * 2, height + bleed * 2).attr({fill: s.gradient('L(0, 0, ' + width + ', ' + height + ')' + gold + '-' + magenta + ':40-' + purple)}));

var rotation = getRandomArbitary(0, 360);
var cos = Math.cos(rotation * Math.PI / 180);
var sin = Math.sin(rotation * Math.PI / 180);

function onCanvas(x, y, r) {
  var px = (x - width / 2) * cos - (y - height / 2) * sin + width / 2;
  var py = (x - width / 2) * sin + (y - height / 2) * cos + height / 2;
  return px > -r && px < width + r && py > -r && py < height + r;
}

// Halftone on a 45 degree lattice, like the screen on a printed can. Each layer
// is one path of circles: thousands of separate elements hang the browser.
function halftone(spacing, color, radiusAt) {
  var d = [];
  var firstRow = Math.floor(-bleed * 2 / spacing);
  for (var row = firstRow; row * spacing / 2 < height + bleed; row++) {
    var shift = row % 2 === 0 ? 0 : spacing / 2;
    for (var x = shift - bleed - spacing; x < width + bleed; x += spacing) {
      var y = row * spacing / 2;
      var r = radiusAt(x, y);
      if (r > 0.3 && onCanvas(x, y, r)) {
        d.push('M' + (x - r).toFixed(1) + ',' + y.toFixed(1) +
          'a' + r.toFixed(2) + ',' + r.toFixed(2) + ' 0 1,0 ' + (r * 2).toFixed(2) + ',0' +
          'a' + r.toFixed(2) + ',' + r.toFixed(2) + ' 0 1,0 ' + (-r * 2).toFixed(2) + ',0');
      }
    }
  }
  background.add(s.path(d.join('')).attr({fill: color}));
}

var diagonal = width * Math.SQRT2;
var goldReach = getRandomArbitary(0.35, 0.55);

// Radii scale with the lattice, so the screen zooms without changing its density
var dotScale = dotSpacing / 9;

halftone(dotSpacing, shade, function(x, y) {
  return dotScale * (1.2 + 2.2 * (x + y) / (width + height));
});

halftone(dotSpacing, gold, function(x, y) {
  var fade = 1 - Math.sqrt(x * x + y * y) / (diagonal * goldReach);
  return dotScale * 4.2 * fade;
});

background.transform('r' + rotation + ',' + width / 2 + ',' + height / 2);

function wavyBundle(centerX, maxSpread) {
  var frequency = getRandomArbitary(0.8, 1.6);
  var phase = getRandomArbitary(0, Math.PI * 2);
  // Every line in a bundle shares one wobble, so they ripple together
  var wobblePhase = getRandomArbitary(0, Math.PI * 2);
  var wobbleSize = getRandomArbitary(4, 12);

  for (var i = 0; i < lineCount; i++) {
    var offset = i / (lineCount - 1) - 0.5;
    var path = '';

    for (var y = -10; y <= height + 10; y += 5) {
      var pinch = 0.72 + 0.28 * Math.sin(y / height * Math.PI * 2 * frequency + phase);
      var wobble = wobbleSize * Math.sin(y / height * Math.PI * 3 + wobblePhase);
      var x = centerX + offset * maxSpread * pinch + wobble;
      path += (path === '' ? 'M' : 'L') + x.toFixed(1) + ',' + y;
    }

    s.path(path).attr({fill: 'none', stroke: silver, strokeWidth: 2.5});
  }
}

var slot = (width - margin * 2) / bundles;
for (var b = 0; b < bundles; b++) {
  wavyBundle(margin + slot * (b + 0.5), slot * 0.8);
}