One commited on
Commit
c3fea4f
·
verified ·
1 Parent(s): 1d6f9d7

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +63 -12
index.html CHANGED
@@ -81,6 +81,8 @@ for(let i=0;i<6;i++){
81
 
82
  // ---- bubbles (tiny, for life) ----
83
  const bubbles=[];for(let i=0;i<14;i++)bubbles.push({x:Math.random(),y:Math.random(),s:0.5+Math.random(),r:0.8+Math.random()*1.6,ph:Math.random()*7});
 
 
84
 
85
  // ---- wax amount (unlabeled slider on the base, just under the LED) ----
86
  // volume multiplier 0.25x..4x; middle = as-is. Radius scales as cbrt(volume).
@@ -123,6 +125,8 @@ function hueRGB(h){ // pleasant saturated RGB from a hue
123
  }
124
  const rgba=(c,a)=>`rgba(${c.r|0},${c.g|0},${c.b|0},${a})`;
125
  const mixWhite=(c,t)=>({r:c.r+(255-c.r)*t,g:c.g+(255-c.g)*t,b:c.b+(255-c.b)*t});
 
 
126
  let LIGHT={...DEF_L},LUM_L=1; // effective lamp colour, updated each frame
127
 
128
  // ---- physics (px units) ----
@@ -263,6 +267,38 @@ function step(dt){
263
  }
264
 
265
  for(const b of bubbles){b.y-=dt*(0.03+0.02*b.s);if(b.y<0){b.y=1;b.x=Math.random();}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  }
267
 
268
  // ---- metaball rendering (gooey, temperature-tinted glow) ----
@@ -313,10 +349,11 @@ function renderBlobs(){
313
  const k=(f-0.3)/0.22;
314
  r=255;g=60*k;bl=40*k;a=110*k*k;
315
  }
316
- if(rgbMix>0&&a>0){ // re-light the wax by the current lamp colour
317
  const lum=0.299*r+0.587*g+0.114*bl;
318
  if(lum>1){const s=Math.min(1.45,lum/LUM_L);
319
- r+=(LIGHT.r*s-r)*rgbMix;g+=(LIGHT.g*s-g)*rgbMix;bl+=(LIGHT.b*s-bl)*rgbMix;}
 
320
  }
321
  // soft tone map to prevent overexposure blips (like a camera sensor flattening)
322
  const tone=v=>v<=230?v:230+25*(1-Math.exp(-(v-230)/8));
@@ -331,18 +368,20 @@ function drawBackground(){
331
  const g=ctx.createRadialGradient(CX,H*0.42,60,CX,H*0.42,H*0.75);
332
  g.addColorStop(0,'#181026');g.addColorStop(1,'#05030a');
333
  ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
334
- // warm light pool from the lamp onto the room
 
335
  ctx.save();ctx.globalCompositeOperation='lighter';
336
  const lg=ctx.createRadialGradient(CX,H-40,20,CX,H-40,H*0.62);
337
- lg.addColorStop(0,rgba(LIGHT,0.16));lg.addColorStop(0.5,rgba(LIGHT,0.05));lg.addColorStop(1,rgba(LIGHT,0));
338
  ctx.fillStyle=lg;ctx.fillRect(0,0,W,H);ctx.restore();
339
  ctx.save();ctx.globalAlpha=0.55;ctx.fillStyle='#000';
340
  ctx.beginPath();ctx.ellipse(CX,H-24,190,26,0,0,Math.PI*2);ctx.fill();ctx.restore();
341
  }
342
  function drawLED(){
 
343
  ctx.save();ctx.globalCompositeOperation='lighter';
344
  const g=ctx.createRadialGradient(CX,H-58,4,CX,H-58,150);
345
- g.addColorStop(0,rgba(LIGHT,0.38));g.addColorStop(1,rgba(LIGHT,0));
346
  ctx.fillStyle=g;ctx.fillRect(CX-170,H-210,340,210);ctx.restore();
347
  }
348
  function drawCap(){
@@ -401,26 +440,31 @@ function drawSlider(){
401
  function drawLiquid(){
402
  ctx.save();ctx.clip(liquidClip);
403
  const g=ctx.createLinearGradient(0,LIQ_TOP,0,LIQ_BOT);
404
- g.addColorStop(0,'#23094a');g.addColorStop(0.5,'#1b0640');g.addColorStop(1,'#12032c');
 
 
 
405
  ctx.fillStyle=g;ctx.fillRect(CX-MAX_HW-8,LIQ_TOP-8,MAX_HW*2+16,LIQ_BOT-LIQ_TOP+16);
406
 
407
- // ---- bottom lamp glow: heat rising up the liquid column ----
 
408
  ctx.globalCompositeOperation='lighter';
409
  const hg=ctx.createLinearGradient(0,LIQ_BOT-190,0,LIQ_BOT+10);
410
  hg.addColorStop(0,rgba(LIGHT,0));
411
- hg.addColorStop(0.6,rgba(LIGHT,0.10));
412
- hg.addColorStop(1,rgba(LIGHT,0.38));
413
  ctx.fillStyle=hg;ctx.fillRect(CX-MAX_HW-8,LIQ_BOT-190,MAX_HW*2+16,200);
414
  const led=ctx.createRadialGradient(CX,LIQ_BOT+6,4,CX,LIQ_BOT+6,150);
415
- led.addColorStop(0,rgba(mixWhite(LIGHT,0.4),0.55));
416
- led.addColorStop(0.35,rgba(LIGHT,0.24));
417
  led.addColorStop(1,rgba(LIGHT,0));
418
  ctx.fillStyle=led;ctx.fillRect(CX-170,LIQ_BOT-150,340,170);
419
 
420
  // drifting shimmer
421
  const sx=CX+Math.sin(time*0.4)*60;
422
  const sh=ctx.createRadialGradient(sx,(LIQ_TOP+LIQ_BOT)/2,10,sx,(LIQ_TOP+LIQ_BOT)/2,220);
423
- sh.addColorStop(0,'rgba(150,90,220,0.10)');sh.addColorStop(1,'rgba(150,90,220,0)');
 
424
  ctx.fillStyle=sh;ctx.fillRect(CX-MAX_HW-8,LIQ_TOP-8,MAX_HW*2+16,LIQ_BOT-LIQ_TOP+16);
425
 
426
  // bubbles
@@ -432,6 +476,13 @@ function drawLiquid(){
432
  ctx.beginPath();ctx.arc(x,y,b.r,0,Math.PI*2);ctx.fill();
433
  }
434
 
 
 
 
 
 
 
 
435
  // ---- the lava: diffuse halo + glowing body ----
436
  renderBlobs();
437
  const bx=CX-MAX_HW-pad,by=LIQ_TOP-pad;
 
81
 
82
  // ---- bubbles (tiny, for life) ----
83
  const bubbles=[];for(let i=0;i<14;i++)bubbles.push({x:Math.random(),y:Math.random(),s:0.5+Math.random(),r:0.8+Math.random()*1.6,ph:Math.random()*7});
84
+ // carbonation: air bubbles shed from the blobs' surfaces (purely cosmetic)
85
+ const blobBubs=[];
86
 
87
  // ---- wax amount (unlabeled slider on the base, just under the LED) ----
88
  // volume multiplier 0.25x..4x; middle = as-is. Radius scales as cbrt(volume).
 
125
  }
126
  const rgba=(c,a)=>`rgba(${c.r|0},${c.g|0},${c.b|0},${a})`;
127
  const mixWhite=(c,t)=>({r:c.r+(255-c.r)*t,g:c.g+(255-c.g)*t,b:c.b+(255-c.b)*t});
128
+ // Blend a fixed colour toward the lamp light's hue as RGB mode fades in (rgbMix 0->1)
129
+ const tint=(orig)=>{const t=0.25*rgbMix;return{r:orig.r*(1-t)+LIGHT.r*t,g:orig.g*(1-t)+LIGHT.g*t,b:orig.b*(1-t)+LIGHT.b*t};};
130
  let LIGHT={...DEF_L},LUM_L=1; // effective lamp colour, updated each frame
131
 
132
  // ---- physics (px units) ----
 
267
  }
268
 
269
  for(const b of bubbles){b.y-=dt*(0.03+0.02*b.s);if(b.y<0){b.y=1;b.x=Math.random();}}
270
+
271
+ // blobs fizz: hotter wax sheds tiny air bubbles off its surface, like
272
+ // carbonation coming out of solution (no interaction with the blobs)
273
+ for(const b of blobs){
274
+ if(Math.random()<dt*(0.08+b.temp*0.3)){
275
+ const rr=b.r*WAXR;
276
+ const ang=Math.random()*Math.PI*2;
277
+ let x=b.x+Math.cos(ang)*rr*(1.02+Math.random()*0.1);
278
+ const y=b.y+Math.sin(ang)*rr*(1.02+Math.random()*0.1);
279
+ const hw=halfWidth(y)-4; // keep inside the glass
280
+ if(Math.abs(x-CX)>hw)x=CX+(x>CX?hw:-hw);
281
+ // spawn a small group of bubbles (like an air pocket bursting)
282
+ const count=2+Math.floor(Math.random()*4); // 2-5 bubbles
283
+ for(let i=0;i<count;i++){
284
+ blobBubs.push({
285
+ x:x+(Math.random()-0.5)*3,
286
+ y:y+(Math.random()-0.5)*3,
287
+ vy:-(16+Math.random()*14),
288
+ r:0.7+Math.random()*1.5,
289
+ ph:Math.random()*7,
290
+ wob:2+Math.random()*2
291
+ });
292
+ }
293
+ }
294
+ }
295
+ for(let i=blobBubs.length-1;i>=0;i--){
296
+ const q=blobBubs[i];
297
+ q.vy-=dt*6; // accelerate upward as it rises
298
+ q.y+=q.vy*dt;
299
+ q.x+=Math.sin(time*3+q.ph)*q.wob*dt; // gentle sideways wiggle
300
+ if(q.y<LIQ_TOP+4||blobBubs.length>120)blobBubs.splice(i,1);
301
+ }
302
  }
303
 
304
  // ---- metaball rendering (gooey, temperature-tinted glow) ----
 
349
  const k=(f-0.3)/0.22;
350
  r=255;g=60*k;bl=40*k;a=110*k*k;
351
  }
352
+ if(rgbMix>0&&a>0){ // re-light the wax by the current lamp colour (50% blend)
353
  const lum=0.299*r+0.587*g+0.114*bl;
354
  if(lum>1){const s=Math.min(1.45,lum/LUM_L);
355
+ const waxBlend=0.25*rgbMix; // only blend 25% toward RGB (halved)
356
+ r+=(LIGHT.r*s-r)*waxBlend;g+=(LIGHT.g*s-g)*waxBlend;bl+=(LIGHT.b*s-bl)*waxBlend;}
357
  }
358
  // soft tone map to prevent overexposure blips (like a camera sensor flattening)
359
  const tone=v=>v<=230?v:230+25*(1-Math.exp(-(v-230)/8));
 
368
  const g=ctx.createRadialGradient(CX,H*0.42,60,CX,H*0.42,H*0.75);
369
  g.addColorStop(0,'#181026');g.addColorStop(1,'#05030a');
370
  ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
371
+ // warm light pool from the lamp onto the room (scaled down in RGB mode)
372
+ const glowScale=1-rgbMix*0.85; // reduce additive glows by 85% in RGB mode (halved effect)
373
  ctx.save();ctx.globalCompositeOperation='lighter';
374
  const lg=ctx.createRadialGradient(CX,H-40,20,CX,H-40,H*0.62);
375
+ lg.addColorStop(0,rgba(LIGHT,0.16*glowScale));lg.addColorStop(0.5,rgba(LIGHT,0.05*glowScale));lg.addColorStop(1,rgba(LIGHT,0));
376
  ctx.fillStyle=lg;ctx.fillRect(0,0,W,H);ctx.restore();
377
  ctx.save();ctx.globalAlpha=0.55;ctx.fillStyle='#000';
378
  ctx.beginPath();ctx.ellipse(CX,H-24,190,26,0,0,Math.PI*2);ctx.fill();ctx.restore();
379
  }
380
  function drawLED(){
381
+ const glowScale=1-rgbMix*0.85; // reduce additive glows by 85% in RGB mode (halved effect)
382
  ctx.save();ctx.globalCompositeOperation='lighter';
383
  const g=ctx.createRadialGradient(CX,H-58,4,CX,H-58,150);
384
+ g.addColorStop(0,rgba(LIGHT,0.38*glowScale));g.addColorStop(1,rgba(LIGHT,0));
385
  ctx.fillStyle=g;ctx.fillRect(CX-170,H-210,340,210);ctx.restore();
386
  }
387
  function drawCap(){
 
440
  function drawLiquid(){
441
  ctx.save();ctx.clip(liquidClip);
442
  const g=ctx.createLinearGradient(0,LIQ_TOP,0,LIQ_BOT);
443
+ // base liquid follows the lamp light's hue in RGB mode; stays purple by default (almost transparent)
444
+ g.addColorStop(0,rgba(tint({r:35,g:9,b:74}),0.15));
445
+ g.addColorStop(0.5,rgba(tint({r:27,g:6,b:64}),0.15));
446
+ g.addColorStop(1,rgba(tint({r:18,g:3,b:44}),0.15));
447
  ctx.fillStyle=g;ctx.fillRect(CX-MAX_HW-8,LIQ_TOP-8,MAX_HW*2+16,LIQ_BOT-LIQ_TOP+16);
448
 
449
+ // ---- bottom lamp glow: heat rising up the liquid column (scaled for subtler RGB) ----
450
+ const glowScale=1-rgbMix*0.85; // reduce additive glows by 85% in RGB mode (halved effect)
451
  ctx.globalCompositeOperation='lighter';
452
  const hg=ctx.createLinearGradient(0,LIQ_BOT-190,0,LIQ_BOT+10);
453
  hg.addColorStop(0,rgba(LIGHT,0));
454
+ hg.addColorStop(0.6,rgba(LIGHT,0.05*glowScale));
455
+ hg.addColorStop(1,rgba(LIGHT,0.18*glowScale));
456
  ctx.fillStyle=hg;ctx.fillRect(CX-MAX_HW-8,LIQ_BOT-190,MAX_HW*2+16,200);
457
  const led=ctx.createRadialGradient(CX,LIQ_BOT+6,4,CX,LIQ_BOT+6,150);
458
+ led.addColorStop(0,rgba(mixWhite(LIGHT,0.4),0.28*glowScale));
459
+ led.addColorStop(0.35,rgba(LIGHT,0.12*glowScale));
460
  led.addColorStop(1,rgba(LIGHT,0));
461
  ctx.fillStyle=led;ctx.fillRect(CX-170,LIQ_BOT-150,340,170);
462
 
463
  // drifting shimmer
464
  const sx=CX+Math.sin(time*0.4)*60;
465
  const sh=ctx.createRadialGradient(sx,(LIQ_TOP+LIQ_BOT)/2,10,sx,(LIQ_TOP+LIQ_BOT)/2,220);
466
+ const shim=tint({r:150,g:90,b:220});
467
+ sh.addColorStop(0,rgba(shim,0.10));sh.addColorStop(1,rgba(shim,0));
468
  ctx.fillStyle=sh;ctx.fillRect(CX-MAX_HW-8,LIQ_TOP-8,MAX_HW*2+16,LIQ_BOT-LIQ_TOP+16);
469
 
470
  // bubbles
 
476
  ctx.beginPath();ctx.arc(x,y,b.r,0,Math.PI*2);ctx.fill();
477
  }
478
 
479
+ // carbonation bubbles shed by the blobs (same look, fade so they "pop")
480
+ for(const q of blobBubs){
481
+ ctx.globalAlpha=Math.min(1,(q.y-LIQ_TOP)/30);
482
+ ctx.beginPath();ctx.arc(q.x,q.y,q.r,0,Math.PI*2);ctx.fill();
483
+ }
484
+ ctx.globalAlpha=1;
485
+
486
  // ---- the lava: diffuse halo + glowing body ----
487
  renderBlobs();
488
  const bx=CX-MAX_HW-pad,by=LIQ_TOP-pad;