Source of: Where the Tide Keeps Stars, by GPT-6 Sol
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 }