Source of: Interplanetary Motion with a dancing twerk, by DeepSeek V4 Pro 0813
299 lines, 49 of them comments ยท 3,221 tokens, written in 23 seconds for $0.0094
1
2 // Interplanetary Motion with a dancing twerk
3 // Planets orbit the Sun according to Kepler's simplified elliptical rules.
4 // A small dancing astronaut twerks endlessly in the bottom-right corner.
5 // Click to reset orbits. Move mouse to change view subtly.
6
7 let planets = [];
8 let sunRadius = 30;
9 let astronaut;
10
11 function setup() {
12 createCanvas(windowWidth, windowHeight);
13
14 // Initialize planets with semi-major axis, eccentricity, angle, speed, color, size
15 // Planet data: [name, a (px), e, startAngle, speedFactor, color, planetRadius, ring?]
16 let planetData = [
17 ['Mercury', 60, 0.2, 0, 2.2, color(180, 180, 180), 5, false],
18 ['Venus', 85, 0.05, 1, 1.6, color(255, 200, 100), 7, false],
19 ['Earth', 115, 0.016, 2.5, 1.3, color(100, 160, 255), 7.5, false],
20 ['Mars', 150, 0.09, 4, 1.0, color(220, 120, 90), 6, false],
21 ['Jupiter', 220, 0.048, 1.7, 0.5, color(230, 180, 130), 16, true],
22 ['Saturn', 290, 0.054, 3.2, 0.35, color(230, 210, 150), 14, true],
23 ['Uranus', 350, 0.047, 5, 0.25, color(140, 210, 230), 10, false],
24 ['Neptune', 400, 0.009, 0.8, 0.2, color(70, 110, 200), 9, false]
25 ];
26
27 for (let data of planetData) {
28 planets.push(new Planet(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]));
29 }
30
31 // Create astronaut for twerking
32 astronaut = new Astronaut(width - 150, height - 150);
33 }
34
35 function draw() {
36 // Background gradient (deep space)
37 drawingContext.shadowBlur = 0; // reset shadows from previous draws
38 background(10, 12, 25);
39
40 // Draw a subtle starfield
41 drawStars();
42
43 // Center of solar system
44 let sunX = width / 2;
45 let sunY = height / 2;
46
47 // Draw the Sun
48 push();
49 translate(sunX, sunY);
50 drawingContext.shadowBlur = 60;
51 drawingContext.shadowColor = color(255, 200, 50, 150);
52 fill(255, 200, 50);
53 noStroke();
54 ellipse(0, 0, sunRadius * 2, sunRadius * 2);
55 drawingContext.shadowBlur = 40;
56 fill(255, 255, 200);
57 ellipse(-5, -5, sunRadius * 0.9, sunRadius * 0.9);
58 pop();
59
60 // Draw orbital paths (elliptical)
61 push();
62 translate(sunX, sunY);
63 stroke(255, 255, 255, 40);
64 noFill();
65 for (let p of planets) {
66 let a = p.a;
67 let e = p.e;
68 let b = a * sqrt(abs(1 - e * e)); // semi-minor axis
69 // Ellipse is centered at origin, but orbit is centered at Sun (focus)
70 // Shift by c = e*a
71 let c = e * a;
72 ellipse(-c, 0, 2 * a, 2 * b);
73 }
74 pop();
75
76 // Update and draw planets
77 for (let p of planets) {
78 p.update(sunX, sunY);
79 p.display(sunX, sunY);
80 }
81
82 // Draw astronaut (twerking)
83 astronaut.update();
84 astronaut.display();
85
86 // Instructions
87 fill(255, 200);
88 noStroke();
89 textSize(14);
90 textAlign(LEFT);
91 text("Click to reset orbits | Mouse to wobble view", 20, 30);
92 }
93
94 function drawStars() {
95 randomSeed(42); // fixed star positions
96 for (let i = 0; i < 200; i++) {
97 let x = random(width);
98 let y = random(height);
99 let r = random(0.5, 2);
100 let alpha = random(80, 180);
101 stroke(255, alpha);
102 strokeWeight(r);
103 point(x, y);
104 }
105 }
106
107 // Planet class using Keplerian approximation
108 class Planet {
109 constructor(name, a, e, startAngle, speedFactor, col, radius, hasRing) {
110 this.name = name;
111 this.a = a; // semi-major axis
112 this.e = e; // eccentricity
113 this.angle = startAngle; // initial angle (mean anomaly approximate)
114 this.speedFactor = speedFactor;
115 this.col = col;
116 this.radius = radius;
117 this.hasRing = hasRing;
118 this.orbitX = 0;
119 this.orbitY = 0;
120 }
121
122 update(sunX, sunY) {
123 // Increment angle based on speed (inverse square root of semi-major axis -> Kepler's third law)
124 this.angle += 0.02 * this.speedFactor;
125
126 // Calculate position in orbit relative to Sun (focus at (0,0), but actually Sun at (0,0), ellipse shifted by c)
127 // Using parametric equation of ellipse shifted by c = ae
128 let c = this.e * this.a;
129 let b = this.a * sqrt(abs(1 - this.e * this.e));
130 let xOrb = this.a * cos(this.angle) - c;
131 let yOrb = b * sin(this.angle);
132
133 // Rotate orbit slightly for variety
134 let rotAngle = this.a * 0.01;
135 let xRot = xOrb * cos(rotAngle) - yOrb * sin(rotAngle);
136 let yRot = xOrb * sin(rotAngle) + yOrb * cos(rotAngle);
137
138 this.orbitX = sunX + xRot;
139 this.orbitY = sunY + yRot;
140 }
141
142 display(sunX, sunY) {
143 push();
144 translate(this.orbitX, this.orbitY);
145
146 // Planet shadow
147 drawingContext.shadowBlur = 15;
148 drawingContext.shadowColor = color(255, 255, 255, 50);
149
150 // Planet body
151 fill(this.col);
152 noStroke();
153 ellipse(0, 0, this.radius * 2, this.radius * 2);
154
155 // Ring for Saturn/Jupiter
156 if (this.hasRing) {
157 push();
158 noFill();
159 stroke(255, 200, 100, 180);
160 strokeWeight(3);
161 ellipse(0, 2, this.radius * 3, this.radius * 1.2);
162 pop();
163 }
164
165 // Small highlight
166 fill(255, 255, 255, 100);
167 ellipse(-this.radius * 0.3, -this.radius * 0.3, this.radius * 0.5, this.radius * 0.5);
168
169 pop();
170
171 // Label for inner planets
172 if (this.a < 200) {
173 fill(255, 180);
174 textSize(11);
175 textAlign(CENTER);
176 text(this.name, this.orbitX, this.orbitY - this.radius - 5);
177 }
178 }
179 }
180
181 // Astronaut that twerks
182 class Astronaut {
183 constructor(x, y) {
184 this.baseX = x;
185 this.baseY = y;
186 this.twerkOffset = 0;
187 this.twerkSpeed = 0.1;
188 this.twerkAmplitude = 8;
189 this.legsAngle = 0;
190 }
191
192 update() {
193 this.twerkOffset = sin(frameCount * 0.3) * this.twerkAmplitude;
194 this.legsAngle = sin(frameCount * 0.5) * 0.7;
195 }
196
197 display() {
198 push();
199 translate(this.baseX + sin(frameCount * 0.1) * 10, this.baseY + this.twerkOffset);
200
201 // Body suit (white with blue details)
202 fill(240);
203 stroke(100);
204 strokeWeight(1);
205
206 // Torso (slightly tilted)
207 push();
208 rotate(sin(frameCount * 0.2) * 0.1);
209 rect(-15, -40, 30, 50, 8);
210
211 // Helmet
212 fill(200, 220, 255);
213 ellipse(0, -50, 32, 32);
214 fill(255);
215 ellipse(0, -48, 22, 22);
216
217 // Visor
218 fill(30, 40, 80, 200);
219 ellipse(4, -48, 14, 10);
220
221 // Backpack
222 fill(180);
223 rect(-25, -30, 12, 35, 5);
224
225 // Arms (flailing with twerk)
226 push();
227 translate(-15, -25);
228 rotate(-0.8 + sin(frameCount * 0.4) * 0.3);
229 fill(240);
230 rect(-12, -5, 12, 20, 5);
231 pop();
232
233 push();
234 translate(15, -25);
235 rotate(0.8 - sin(frameCount * 0.4) * 0.3);
236 fill(240);
237 rect(0, -5, 12, 20, 5);
238 pop();
239
240 // Legs (twerking motion)
241 push();
242 translate(-8, 10);
243 rotate(this.legsAngle);
244 fill(240);
245 rect(-6, 0, 12, 25, 3);
246 // Boot
247 fill(100);
248 rect(-8, 20, 16, 10, 3);
249 pop();
250
251 push();
252 translate(8, 10);
253 rotate(-this.legsAngle);
254 fill(240);
255 rect(-6, 0, 12, 25, 3);
256 fill(100);
257 rect(-8, 20, 16, 10, 3);
258 pop();
259
260 // Tiny flag on backpack
261 fill(255, 100, 100);
262 triangle(-25, -30, -15, -25, -25, -15);
263
264 pop();
265
266 // Twerk lines effect
267 if (this.twerkOffset > 2) {
268 stroke(255, 180, 0, 150);
269 strokeWeight(2);
270 line(this.baseX - 20, this.baseY + 35, this.baseX - 20, this.baseY + 45 + this.twerkOffset * 0.5);
271 line(this.baseX + 20, this.baseY + 35, this.baseX + 20, this.baseY + 45 + this.twerkOffset * 0.5);
272 }
273
274 // Shadow on floor
275 fill(0, 80);
276 noStroke();
277 ellipse(this.baseX, this.baseY + 50, 60, 15);
278 }
279 }
280
281 // Reset orbits on click
282 function mousePressed() {
283 planets = [];
284 setup();
285 }
286
287 // Make canvas responsive
288 function windowResized() {
289 resizeCanvas(windowWidth, windowHeight);
290 // Reinitialize planets relative to new center, but keep astronaut position relative to bottom-right
291 let newAstronautX = width - 150;
292 let newAstronautY = height - 150;
293 if (astronaut) {
294 astronaut.baseX = newAstronautX;
295 astronaut.baseY = newAstronautY;
296 }
297 // For simplicity, keep existing planets but they will recalc relative to new center in update
298 }
299