Primer
to generate a new and unique variation
Every variart piece is a small program. This is the one that drew what you're looking at.
var background = '#E8DFD3';
var colors = ['#D9483B', '#EDB92E', '#1E68A0', '#1A1718'];
var margin = 80;
var grid = 5;
var blocks = intVariable('Blocks', 5, 16);
var curves = intVariable('Curves', 0, 60);
var zoom = [100, 120, 250][intVariable('Zoom', 1, 3) - 1];
s.rect(0, 0, width, height).attr({fill: background});
// Three steps rather than a continuum: in between, the cluster ends just short
// of the edge, in the wrap, which reads as a mistake rather than a crop.
var size = (width - margin * 2) / grid * zoom / 100;
var has = function(r, c){ return !!filled[r + ',' + c]; };
// Grow a connected cluster from the middle of the grid.
var mid = Math.floor(grid / 2);
var cells = [{r: mid, c: mid}];
var filled = {};
filled[mid + ',' + mid] = true;
while (cells.length < blocks){
var from = cells[getRandomInt(0, cells.length - 1)];
var d = [[0, 1], [0, -1], [1, 0], [-1, 0]][getRandomInt(0, 3)];
var r = from.r + d[0], c = from.c + d[1];
if (r >= 0 && r < grid && c >= 0 && c < grid && !filled[r + ',' + c]){
filled[r + ',' + c] = true;
cells.push({r: r, c: c});
}
}
// Centre the cluster, whatever shape it grew into.
var minR = grid, maxR = 0, minC = grid, maxC = 0;
cells.forEach(function(cell){
minR = Math.min(minR, cell.r); maxR = Math.max(maxR, cell.r);
minC = Math.min(minC, cell.c); maxC = Math.max(maxC, cell.c);
});
var offsetX = (width - (maxC - minC + 1) * size) / 2 - minC * size;
var offsetY = (height - (maxR - minR + 1) * size) / 2 - minR * size;
// The square corner of a curve tucks in against its neighbours, so the arc
// faces open space the way an arch does.
function cornerFor(r, c){
var right = has(r, c + 1) && !has(r, c - 1) ? 1 : (has(r, c - 1) && !has(r, c + 1) ? 0 : getRandomInt(0, 1));
var bottom = has(r + 1, c) && !has(r - 1, c) ? 1 : (has(r - 1, c) && !has(r + 1, c) ? 0 : getRandomInt(0, 1));
return bottom * 2 + right;
}
// Neighbours never share a colour, so every block reads on its own.
var colorAt = {};
function pickColor(r, c){
var taken = [colorAt[(r - 1) + ',' + c], colorAt[(r + 1) + ',' + c],
colorAt[r + ',' + (c - 1)], colorAt[r + ',' + (c + 1)]];
var options = colors.filter(function(k){ return taken.indexOf(k) == -1; });
return options[getRandomInt(0, options.length - 1)];
}
// A quarter circle filling the cell, its right angle in one corner.
function quarter(x, y, corner, fill){
var cx = x + (corner % 2) * size, cy = y + Math.floor(corner / 2) * size;
var sx = corner % 2 ? -1 : 1, sy = corner < 2 ? 1 : -1;
s.path('M' + cx + ',' + cy + ' L' + (cx + sx * size) + ',' + cy +
' A' + size + ',' + size + ' 0 0,' + (sx * sy > 0 ? 1 : 0) + ' ' + cx + ',' + (cy + sy * size) + ' Z')
.attr({fill: fill, stroke: fill, strokeWidth: 1});
}
cells.forEach(function(cell){
var fill = pickColor(cell.r, cell.c);
colorAt[cell.r + ',' + cell.c] = fill;
var x = offsetX + cell.c * size, y = offsetY + cell.r * size;
if (getRandomInt(1, 100) <= curves){
quarter(x, y, cornerFor(cell.r, cell.c), fill);
} else {
// The hairline stroke closes the anti-aliasing seams between tiles.
s.rect(x, y, size, size).attr({fill: fill, stroke: fill, strokeWidth: 1});
}
});
Copyright © 2014–2026 Logic and Matter. All rights reserved. Questions? Comments? hello@variart.io