let cool1 = "https://coolors.co/c2efb3-97abb1-746f72-735f3d-594a26".split("/").pop().split("-").map((a) => "#" + a);
let cool11 = "https://coolors.co/f1fbee-c5d0d3-a5a1a4-b2986c-9d8243".split("/").pop().split("-").map((a) => "#" + a);
let cool2 = "https://coolors.co/e2dadb-dae2df-a2a7a5-6d696a-ffffff".split("/").pop().split("-").map((a) => "#" + a);
let cool21 = "https://coolors.co/f6f4f4-f4f6f5-cacecc-9b9798-c2c2c2".split("/").pop().split("-").map((a) => "#" + a);
let cool3 = "https://coolors.co/a63446-fbfef9-0c6291-000004-7e1946".split("/").pop().split("-").map((a) => "#" + a);
let cool31 = "https://coolors.co/ce6474-d0f3b9-129ae2-000066-cc2872".split("/").pop().split("-").map((a) => "#" + a);
let x, y, xp, yp, xx, yy;
let traceCubeLines = false;
let stormCloud, xcub, ycub;
colorMode(HSB, 360, 100, 100, 100);
seed = int(Math.random() * 9999999);
createCanvas(iw, iw / ar);
createCanvas(ih * ar, ih);
col1 = random([cool1, cool2, cool3]);
col2 = random([cool11, cool21, cool31]);
for (let i = 0; i < 7; i++) {
let x = random(width / 5, 4 * width / 5);
let y = height - random(50, 200);
let length = random(20, 100);
let angle = random(-PI/4, PI/4);
let pole = {x: x, y: y, length: length, angle: angle};
for (let i = 0; i < cloudCount; i++) {
let y = map(i, 0, cloudCount, 0, height / 2);
let cloud = createCloud(y);
for (let i = 0; i < stormCloudCount; i++) {
let y = map(i, 0, stormCloudCount, 0, height / 3);
stormCloud = createStormCloud(y);
stormClouds.push(stormCloud);
for (let i = 0; i < duneCount; i++) {
let y = map(i, 0, duneCount, height / 2, height);
let dune = createDune(y);
for (let i = 0; i < birdCount; i++) {
for (let cloud of clouds) {
strokeWeight(random(0.25, 3));
for (let stormCloud of stormClouds) {
for (let pt of stormCloud) {
let cubeColor = color(random(col2));
let rwis = wisW / sizeMod;
let offsetY = csize / 2 * sizeMod;
draw3DCube(pt.x, pt.y, csize, cubeColor, 0, 0);
for (let dune of dunes) {
let h = map(pt.y, pt.duneY, pt.duneY + duneHeight, 100, 30);
strokeWeight(random(0.25, 1.5));
for (let pole of poles) {
translate(pole.x, pole.y);
line(0, 0, pole.length, 0);
let numCables = floor(random(1, 5));
for (let i = 0; i < numCables; i++) {
let cableAttachPointX = pole.x + cos(pole.angle) * pole.length / numCables * i;
let cableAttachPointY = pole.y + sin(pole.angle) * pole.length / numCables * i;
let lowestCloudPoint = getLowestCloudPoint(cableAttachPointX - 100, cableAttachPointX + 100);
let cp1 = createVector(cableAttachPointX, cableAttachPointY - 50 + i * 10);
let cp2 = createVector(lowestCloudPoint.x, lowestCloudPoint.y + 50 + random(0,150));
drawingContext.setLineDash([0, 2, 1]);
strokeWeight(floor(random(1.5, 3.5)));
bezier(cableAttachPointX, cableAttachPointY, cp1.x - 15, cp1.y + 15, cp2.x + 10, cp2.y, lowestCloudPoint.x, lowestCloudPoint.y - random(0, 80));
for (let bird of birds) {
strokeWeight(random(0.25, 1.5));
birdWings = bird.size / 2;
line(bird.x - birdWings, bird.y, bird.x, bird.y + birdWings);
line(bird.x, bird.y + birdWings, bird.x + birdWings, bird.y);
line(bird.x - bird.size / 2, bird.y, bird.x + bird.size / 2, bird.y);
applyMonochromaticGrain(gS);
rect(bd / 2, bd / 2, width - bd, height - bd);
let x = random(width / 1.5);
let y = random(height / 3);
let size = random(8, 12);
let waveAmp = duneHeight * 0.25;
let waveFreq = TWO_PI / width;
for (let x = 0; x < width; x += 1) {
for (let yOff = 0; yOff < duneHeight; yOff += 1) {
let yNoise = noise(x * 0.003, (y + yOff) * 0.004) * duneHeight;
let yWave = waveAmp * sin(waveFreq * x);
yWave = waveAmp * sin(waveFreq * x - phase);
let pt = createVector(x, y + yOff + yNoise + yWave);
function createCloud(y) {
for (let x = 0; x < width; x += 5) {
for (let yOff = 0; yOff < cloudHeight; yOff += 5) {
let yNoise = noise(x * 0.01, (y + yOff) * 0.01) * cloudHeight;
let pt = createVector(x, y + yOff + yNoise);
function createStormCloud(y) {
let cloudWidth = width / floor(random(2,3));
let offsetX = width / random(4, 6);
let amplitude = cloudWidth / 8;
for (let x = offsetX; x < offsetX + cloudWidth; x += 5) {
let normalizedX = (x - offsetX) / cloudWidth;
let edgeFactor = Math.cos(normalizedX * PI);
let numPoints = max(1, map(edgeFactor, 0, 1, 1, 5));
let widthVariation = amplitude * (noise(x * 0.01) * 2 - 1);
for (let yOff = 0; yOff < cloudHeight; yOff += numPoints) {
let yNoise = (noise(x * 0.01, (y + yOff) * 0.01) * 2 - 1) * cloudHeight;
let pt = createVector(x + widthVariation, y + yOff + yNoise);
function getLowestCloudPoint(startX, endX) {
for (let stormCloud of stormClouds) {
for (let pt of stormCloud) {
if (pt.x >= startX && pt.x <= endX && pt.y > lowestY) {
function getClosestCloudPoint(pole) {
let closestDist = Infinity;
for (let stormCloud of stormClouds) {
for (let pt of stormCloud) {
let d = p5.Vector.dist(pt, pole);
saveCanvas('dunes_dc_' + floor(random(100)), 'png');