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