Sketches

I made some art.


The following images were all generated with the same script, which I’ve provided in full below. You can run this in p5.js or — with adaptations — in Processing.

The generative art follows a simple procedure:

  • Pick either the dark or the light side.
  • Randomly find a decent place to put a new triangle. “Decent” here means that a dark triangle goes in a darker area, and a lighter triangle prefers a lighter area.
  • Draw the triangle there (you can choose between edgey triangles, triangles with no edges, or drawing only circles at the vertices of where the triangle would go.)
  • Repeat.

This simple pattern generates some wild patterns. All of these images are free to download and use for anything you like. Let me know if you do something really cool with the images or code!

Live Demo

There is a live version here that generates a 600×600 magenta sample.

p5.js code

COLORS = {
    background: [142, 20, 100],
    steelblue: [20, 100, 142],
    turq: [20, 142, 100],
    magenta: [142, 20, 100],
    black: [2, 2, 2],
};

CONFIG = {
    MINIMIZE: true,
    GROWTHMODE: "CENTER", // "UNIFORM", "TOPRIGHT"
    TRYCOUNT: 5,
    RANDCOUNT: 10e5,
    MAXRAD: 50,
    GROWTHRATE: 0.1,
    SHAPE: "TRIANGLE", // "LINES3", "CIRCLE",
};

let _randoms = [];
for (var i = 0; i < CONFIG.RANDCOUNT; i++) {
    _randoms.push(Math.random())
}

let _randI = 0;
function urandom(i, j) {
    let val = _randoms[_randI++ % _randoms.length];
    if (i && !j) {
        return val * i;
    } else if (i && j) {
        return (val * (j - i)) + i;
    } else {
        return val;
    }
}

function uget(i, j) {
    // return get(i, j)
    i = int(i);
    j = int(j);
    let d = pixelDensity();
    return [
        pixels[(d * ((i+j*width) * 4))+0],
        pixels[(d * ((i+j*width) * 4))+1],
        pixels[(d * ((i+j*width) * 4))+2],
    ]
}

function setup() {
    createCanvas(500, 500);
    pixelDensity(1);
    background(COLORS.black);
    CONFIG.MAXRAD = width / 80;
    // background(10);
    // noStroke();
    // fill(245);
    // triangle(0, 0, 0, height, width, height);
}

let frame = 0;
function draw() {

    frame += 1;

    if (frame % 2 == 0) {
        CONFIG.MINIMIZE = !CONFIG.MINIMIZE;
    }
    if (frame/CONFIG.GROWTHRATE > width && frame/CONFIG.GROWTHRATE > height) {
        frame = 0;
    }

    let pts = [0, 0, 0, 0, 0, 0];
    loadPixels();
    for (var d = 0; d < pts.length; d += 2) {
        let minSoFar = 10000;
        let maxSoFar = 0;
        for (var i = 0; i < CONFIG.TRYCOUNT; i++) {

            let rndX, rndY;

            if (d == 0) {
                if (CONFIG.GROWTHMODE == "UNIFORM") {
                    rndX = urandom(CONFIG.MAXRAD, width - CONFIG.MAXRAD);
                    rndY = urandom(CONFIG.MAXRAD, height - CONFIG.MAXRAD);
                } else if (CONFIG.GROWTHMODE == "TOPRIGHT") {
                    rndX = urandom(frame/CONFIG.GROWTHRATE);
                    rndY = urandom(frame/CONFIG.GROWTHRATE);
                } else if (CONFIG.GROWTHMODE == "CENTER") {
                    rndX = width/2 + (urandom(
                        -1 * frame/CONFIG.GROWTHRATE,
                        frame/CONFIG.GROWTHRATE
                    ) / 2);
                    rndY = height/2 + (urandom(
                        -1 * frame/CONFIG.GROWTHRATE,
                        frame/CONFIG.GROWTHRATE
                    ) / 2);
                }
            } else {
                rndX = urandom(pts[0] - CONFIG.MAXRAD, pts[0] + CONFIG.MAXRAD);
                rndY = urandom(pts[1] - CONFIG.MAXRAD, pts[1] + CONFIG.MAXRAD);
            }

            let v2 = uget(rndX, rndY);
            // let v2 = get(rndX, rndY);
            let val = v2[0] + v2[1] + v2[2];

            if (CONFIG.MINIMIZE) {
                if (val < minSoFar) {
                    minSoFar = val;
                    pts[d + 0] = rndX; pts[d + 1] = rndY;
                }
            } else {
                if (val > maxSoFar) {
                    maxSoFar = val;
                    pts[d + 0] = rndX; pts[d + 1] = rndY;
                }
            }
        }
    }

    noStroke();
    if (CONFIG.MINIMIZE) {
        let r = urandom(100);
        let g = urandom(100-r);
        let b = 100 - (r + g);

        fill(r, g, b, 50);
        stroke(r, g, b, 50);
        // fill(50, 0, 0, 50);
    } else {
        let r = urandom(100);
        let g = urandom(100-r);
        let b = 100 - (r + g);

        fill(255 - r, 255 - g, 255 - b, 50);
        stroke(255 - r, 255 - g, 255 - b, 50);
        // fill(150, 150, 255, 50);
    }

    if (CONFIG.SHAPE == "TRIANGLE") {
        noStroke();
        triangle(
            pts[0], pts[1],
            pts[2], pts[3],
            pts[4], pts[5],
        );
    } else if (CONFIG.SHAPE == "CIRCLE") {
        noStroke();
        ellipse(
            pts[0], pts[1],
            5, 5
        );
        ellipse(
            pts[2], pts[3],
            5, 5
        );
        ellipse(
            pts[4], pts[5],
            5, 5
        );
    } else if (CONFIG.SHAPE == "LINES3") {
        strokeWeight(4)
        line(
            pts[0], pts[1],
            pts[2], pts[3],
        );
        line(
            pts[2], pts[3],
            pts[4], pts[5],
        );
        line(
            pts[4], pts[5],
            pts[0], pts[1],
        );
    }

}
Written on January 18, 2018