146 lines, 5 of them comments ยท 1,850 tokens, written in 25 seconds for $0.048


  1  let jellyfish = [];
  2  let particles = [];
  3  
  4  function setup() {
  5    createCanvas(windowWidth, windowHeight);
  6    colorMode(HSB, 360, 100, 100, 100);
  7    for (let i = 0; i < 6; i++) {
  8      jellyfish.push(new Jellyfish(random(width), random(height)));
  9    }
 10  }
 11  
 12  function draw() {
 13    background(240, 60, 8, 25);
 14    
 15    // stars
 16    randomSeed(42);
 17    for (let i = 0; i < 150; i++) {
 18      let sx = random(width);
 19      let sy = random(height);
 20      let sb = 40 + 60 * sin(frameCount * 0.02 + i);
 21      noStroke();
 22      fill(60, 10, sb, 50);
 23      circle(sx, sy, random(1, 3));
 24    }
 25    
 26    for (let j of jellyfish) {
 27      j.update();
 28      j.display();
 29    }
 30    
 31    for (let i = particles.length - 1; i >= 0; i--) {
 32      particles[i].update();
 33      particles[i].display();
 34      if (particles[i].life <= 0) particles.splice(i, 1);
 35    }
 36  }
 37  
 38  class Jellyfish {
 39    constructor(x, y) {
 40      this.x = x;
 41      this.y = y;
 42      this.baseHue = random(160, 320);
 43      this.size = random(40, 80);
 44      this.speed = random(0.3, 0.8);
 45      this.wobble = random(1000);
 46      this.tentacles = [];
 47      this.numTentacles = floor(random(5, 9));
 48      for (let i = 0; i < this.numTentacles; i++) {
 49        this.tentacles.push([]);
 50      }
 51      this.phase = random(TWO_PI);
 52    }
 53    
 54    update() {
 55      let mx = (mouseX - this.x) * 0.003;
 56      let my = (mouseY - this.y) * 0.003;
 57      this.wobble += 0.01;
 58      this.x += sin(this.wobble * 1.3) * 0.8 + mx;
 59      this.y -= this.speed + sin(this.wobble) * 0.3 - my * 0.5;
 60      this.phase += 0.05;
 61      
 62      if (this.y < -this.size * 3) this.y = height + this.size * 2;
 63      if (this.x < -this.size * 2) this.x = width + this.size * 2;
 64      if (this.x > width + this.size * 2) this.x = -this.size * 2;
 65      
 66      for (let i = 0; i < this.numTentacles; i++) {
 67        let tx = this.x + map(i, 0, this.numTentacles - 1, -this.size * 0.6, this.size * 0.6);
 68        let ty = this.y + this.size * 0.3;
 69        this.tentacles[i].unshift({x: tx, y: ty});
 70        if (this.tentacles[i].length > 30) this.tentacles[i].pop();
 71      }
 72      
 73      if (random() < 0.1) {
 74        particles.push(new Particle(this.x + random(-this.size/2, this.size/2), this.y, this.baseHue));
 75      }
 76    }
 77    
 78    display() {
 79      push();
 80      let pulse = sin(this.phase) * 0.15 + 1;
 81      let sw = this.size * pulse;
 82      let sh = this.size * 0.7 * (1 / pulse);
 83      
 84      // glow
 85      for (let g = 3; g > 0; g--) {
 86        noStroke();
 87        fill(this.baseHue, 70, 80, 5);
 88        ellipse(this.x, this.y, sw * (1 + g * 0.4), sh * (1 + g * 0.4));
 89      }
 90      
 91      // body
 92      noStroke();
 93      for (let i = 10; i > 0; i--) {
 94        let inter = i / 10;
 95        fill(this.baseHue, 50 + inter * 30, 60 + inter * 30, 40 + inter * 30);
 96        ellipse(this.x, this.y, sw * inter, sh * inter);
 97      }
 98      
 99      // dome highlight
100      fill(this.baseHue, 20, 100, 20);
101      ellipse(this.x - sw * 0.15, this.y - sh * 0.15, sw * 0.3, sh * 0.25);
102      
103      // tentacles
104      for (let i = 0; i < this.numTentacles; i++) {
105        let t = this.tentacles[i];
106        noFill();
107        for (let w = 3; w > 0; w--) {
108          stroke(this.baseHue, 60, 70, 10 + w * 5);
109          strokeWeight(w * 1.5);
110          beginShape();
111          for (let j = 0; j < t.length; j++) {
112            let wavex = sin(j * 0.3 + this.wobble * 2 + i) * (j * 0.5);
113            curveVertex(t[j].x + wavex, t[j].y + j * 2);
114          }
115          endShape();
116        }
117      }
118      pop();
119    }
120  }
121  
122  class Particle {
123    constructor(x, y, hue) {
124      this.x = x;
125      this.y = y;
126      this.hue = hue + random(-20, 20);
127      this.life = 100;
128      this.vx = random(-0.5, 0.5);
129      this.vy = random(-1, 0.5);
130      this.size = random(2, 5);
131    }
132    update() {
133      this.x += this.vx;
134      this.y += this.vy;
135      this.life -= 1.2;
136    }
137    display() {
138      noStroke();
139      fill(this.hue % 360, 60, 80, this.life * 0.5);
140      circle(this.x, this.y, this.size * (this.life / 100));
141    }
142  }
143  
144  function windowResized() {
145    resizeCanvas(windowWidth, windowHeight);
146  }