createCanvas(1000, 1000);
colorMode(HSB, 360, 100, 100, 100);
for (let i = 0; i < 7; i++) {
let x = random(width / 4, 3 * width / 4);
let y = height - random(50, 200);
let length = random(50, 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);
stormCloud = createStormCloud(height / 5);
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 pt of stormCloud) {
strokeWeight(random(2, 5));
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);
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 createStormCloudFirst(y) {
let cloudWidth = width / random(1.5, 2.5);
let amplitude = cloudWidth / 8;
for (let x = offsetX; x < offsetX + cloudWidth; x += 5) {
let widthVariation = amplitude * (noise(x * 0.01) * 2 - 1);
for (let yOff = 0; yOff < cloudHeight; yOff += 5) {
let yNoise = (noise(x * 0.01, (y + yOff) * 0.01) * 2 - 1) * cloudHeight;
let pt = createVector(x + widthVariation, y + yOff + yNoise);
function createStormCloud(y) {
let cloudWidth = width / 2;
let offsetX = width / random(4,8);
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 getClosestCloudPoint(pole) {
let closestDist = Infinity;
for (let pt of stormCloud) {
let d = p5.Vector.dist(pt, pole);
function getLowestCloudPoint(startX, endX) {
for (let pt of stormCloud) {
if (pt.x >= startX && pt.x <= endX && pt.y > lowestY) {
saveCanvas('dunes_da_' + floor(random(100)), 'png');