159 lines, 9 of them comments ยท 2,709 tokens, written in 27 seconds for $0.027


  1  let stars = [];
  2  let glimmers = [];
  3  
  4  function setup() {
  5    createCanvas(windowWidth, windowHeight);
  6    pixelDensity(Math.min(window.devicePixelRatio || 1, 2));
  7    randomSeed(42);
  8    noiseSeed(17);
  9  
 10    for (let i = 0; i < 180; i++) {
 11      stars.push({
 12        x: random(35, 765),
 13        y: random(25, 440),
 14        r: random(0.5, 2),
 15        phase: random(TWO_PI)
 16      });
 17    }
 18    for (let i = 0; i < 38; i++) {
 19      glimmers.push({
 20        x: random(60, 740),
 21        y: random(535, 785),
 22        phase: random(TWO_PI),
 23        speed: random(0.3, 0.9)
 24      });
 25    }
 26  }
 27  
 28  function draw() {
 29    background(5, 12, 29);
 30    const s = min(width, height) / 800;
 31    translate(width / 2, height / 2);
 32    scale(s);
 33    translate(-400, -400);
 34    const t = frameCount * 0.012;
 35  
 36    // Deep twilight sky.
 37    const sky = drawingContext.createLinearGradient(0, 0, 0, 650);
 38    sky.addColorStop(0, '#070d23');
 39    sky.addColorStop(0.55, '#18334c');
 40    sky.addColorStop(1, '#426469');
 41    drawingContext.fillStyle = sky;
 42    noStroke();
 43    rect(0, 0, 800, 650);
 44  
 45    // A faint, broad veil of color behind the moon.
 46    const haze = drawingContext.createRadialGradient(535, 280, 20, 535, 280, 350);
 47    haze.addColorStop(0, 'rgba(91,194,184,0.19)');
 48    haze.addColorStop(1, 'rgba(91,194,184,0)');
 49    drawingContext.fillStyle = haze;
 50    rect(0, 0, 800, 620);
 51  
 52    noStroke();
 53    for (const star of stars) {
 54      const flicker = 0.65 + 0.35 * sin(t * 1.7 + star.phase);
 55      fill(207, 241, 231, 45 + 150 * flicker);
 56      circle(star.x, star.y, star.r * (1 + 0.2 * flicker));
 57    }
 58  
 59    // Moonlight bloom and moon.
 60    for (let i = 11; i > 0; i--) {
 61      fill(162, 241, 219, 2.8);
 62      circle(540, 238, 135 + i * 23);
 63    }
 64    const moon = drawingContext.createRadialGradient(520, 215, 5, 540, 238, 73);
 65    moon.addColorStop(0, '#fff9db');
 66    moon.addColorStop(0.65, '#e2f3db');
 67    moon.addColorStop(1, '#a9d9cf');
 68    drawingContext.fillStyle = moon;
 69    circle(540, 238, 140);
 70  
 71    // Three distant ridgelines.
 72    ridge(408, 52, 0.006, color(29, 65, 74), 0.1, t);
 73    ridge(463, 45, 0.009, color(19, 53, 65), 1.8, t);
 74    ridge(518, 35, 0.013, color(11, 39, 54), 4.1, t);
 75  
 76    // Still water, fading toward the viewer.
 77    const sea = drawingContext.createLinearGradient(0, 520, 0, 800);
 78    sea.addColorStop(0, '#173f52');
 79    sea.addColorStop(0.45, '#0c2a41');
 80    sea.addColorStop(1, '#07172b');
 81    drawingContext.fillStyle = sea;
 82    noStroke();
 83    rect(0, 520, 800, 280);
 84  
 85    // Scattered horizontal fragments of the moon's reflection.
 86    for (let i = 0; i < 95; i++) {
 87      const y = 525 + i * 3.15;
 88      const spread = (y - 520) * 0.52;
 89      const x = 540 + sin(i * 8.37 + t * 0.65) * spread * 0.55;
 90      const len = (5 + (i % 7) * 3.4) * (0.5 + (y - 520) / 310);
 91      const alpha = 100 * (1 - (y - 520) / 450) * (0.55 + 0.45 * sin(i * 3.1 + t * 2));
 92      stroke(182, 244, 218, alpha);
 93      strokeWeight(i % 9 === 0 ? 2 : 1);
 94      line(x - len / 2, y, x + len / 2, y);
 95    }
 96  
 97    // Quiet wave contours.
 98    noFill();
 99    for (let j = 0; j < 17; j++) {
100      const y = 542 + j * 17;
101      stroke(110, 193, 182, 10 + j * 0.8);
102      strokeWeight(1);
103      beginShape();
104      for (let x = -10; x <= 810; x += 12) {
105        vertex(x, y + 2.5 * sin(x * 0.017 + j * 1.8 + t));
106      }
107      endShape();
108    }
109  
110    // Small floating lights above the water.
111    noStroke();
112    for (const g of glimmers) {
113      const x = g.x + 8 * sin(t * g.speed + g.phase);
114      const y = g.y + 5 * cos(t * g.speed + g.phase);
115      const a = 35 + 80 * max(0, sin(t * 1.4 + g.phase));
116      fill(153, 247, 204, a * 0.18);
117      circle(x, y, 13);
118      fill(202, 255, 218, a);
119      circle(x, y, 2.2);
120    }
121  
122    // Foreground reeds frame the view.
123    for (let side of [-1, 1]) {
124      for (let i = 0; i < 22; i++) {
125        const baseX = side === -1 ? -18 + i * 8 : 818 - i * 8;
126        const h = 65 + (i * 37) % 115;
127        const lean = side * (18 + (i % 5) * 8) + 9 * sin(t * 0.8 + i);
128        const tipX = baseX + lean;
129        const tipY = 810 - h;
130        stroke(5, 23, 35, 200);
131        strokeWeight(1.4 + (i % 3) * 0.6);
132        noFill();
133        bezier(baseX, 814, baseX + lean * 0.15, 765, tipX - lean * 0.3, tipY + 28, tipX, tipY);
134        if (i % 4 === 0) {
135          noStroke();
136          fill(7, 29, 38, 220);
137          ellipse(tipX, tipY - 5, 5, 19);
138        }
139      }
140    }
141  }
142  
143  function ridge(base, amplitude, frequency, shade, offset, t) {
144    noStroke();
145    fill(shade);
146    beginShape();
147    vertex(0, 800);
148    for (let x = 0; x <= 800; x += 8) {
149      const n = noise(x * frequency, offset);
150      const y = base - n * amplitude + 9 * sin(x * 0.012 + offset);
151      vertex(x, y);
152    }
153    vertex(800, 800);
154    endShape(CLOSE);
155  }
156  
157  function windowResized() {
158    resizeCanvas(windowWidth, windowHeight);
159  }