🎮ArcadeLab

Gravity Lab - Build Your Solar System

by CosmicLion36
271 lines8.0 KB
▶ Play

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Gravity Lab</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#050711;color:#f4f5ff;font-family:Inter,Arial,sans-serif;overflow:hidden}
header{height:76px;padding:15px 22px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #20243b;background:#090c19}
.logo{font-size:20px;font-weight:900;letter-spacing:2px}
.logo span{color:#a78bfa}
.sub{font-size:10px;color:#858ca8;letter-spacing:2px;margin-top:5px}
button{background:#171c32;border:1px solid #343b59;color:white;padding:10px 14px;border-radius:9px;cursor:pointer}
button:hover{background:#292f4e}
button.active{background:#7048d9;border-color:#a78bfa}
main{display:grid;grid-template-columns:1fr 270px;height:calc(100dvh - 76px)}
#space{width:100%;height:100%;display:block;touch-action:none}
.panel{background:#0b0e1e;border-left:1px solid #252943;padding:20px;overflow:auto}
h3{font-size:12px;letter-spacing:2px;color:#a9afd0}
.stat{display:flex;justify-content:space-between;margin:18px 0;color:#a8afc9;font-size:13px}
.stat strong{color:white}
.control{margin:25px 0}
.control label{font-size:12px;color:#aab1cd;display:flex;justify-content:space-between;margin-bottom:12px}
input[type=range]{width:100%;accent-color:#a78bfa}
.actions{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.actions button{font-size:12px}
.hint{font-size:11px;line-height:1.7;color:#777f9d;margin-top:24px}
footer{position:absolute;bottom:14px;left:18px;color:#68708e;font-size:10px;pointer-events:none;letter-spacing:1px}
@media(max-width:650px){
 main{grid-template-columns:1fr;grid-template-rows:1fr auto}
 .panel{border-left:0;border-top:1px solid #252943;padding:12px 16px;max-height:220px}
 .control{margin:12px 0}
 .hint{display:none}
 header{height:65px}
 main{height:calc(100dvh - 65px)}
}
</style>
</head>
<body>
<header>
 <div>
  <div class="logo">GRAVITY<span>LAB</span> 🪐</div>
  <div class="sub">CODE PLAYGROUND / EPISODE 001</div>
 </div>
 <div class="actions" style="display:flex">
  <button id="pause">Ⅱ Pause</button>
  <button id="reset">↻ Reset</button>
 </div>
</header>

<main>
 <div style="position:relative;min-height:0">
  <canvas id="space"></canvas>
  <footer>CLICK TO CREATE PLANETS · DRAG TO MOVE</footer>
 </div>

 <aside class="panel">
  <h3>UNIVERSE CONTROL</h3>
  <div class="stat"><span>Planets</span><strong id="count">0</strong></div>
  <div class="stat"><span>Simulation</span><strong id="status">RUNNING</strong></div>

  <div class="control">
   <label>Gravity <strong id="gv">1.0x</strong></label>
   <input id="gravity" type="range" min="0" max="3" step=".1" value="1">
  </div>

  <div class="control">
   <label>Time speed <strong id="sv">1.0x</strong></label>
   <input id="speed" type="range" min=".1" max="3" step=".1" value="1">
  </div>

  <div class="control">
   <label>Planet size <strong id="rv">12</strong></label>
   <input id="radius" type="range" min="5" max="25" value="12">
  </div>

  <h3>QUICK ACTIONS</h3>
  <div class="actions">
   <button id="add">+ Add planet</button>
   <button id="sun">☀ Add star</button>
   <button id="clear">✕ Clear all</button>
   <button id="burst">✦ Random system</button>
  </div>
  <p class="hint">
   EXPERIMENT 001<br><br>
   Gravity is the invisible force that attracts objects with mass. Create your own planetary system and observe how bodies move through space.<br><br>
   Try adding two large planets close together. Watch what happens!
  </p>
 </aside>
</main>

<script>
const canvas=document.getElementById('space');
const ctx=canvas.getContext('2d');
const $=id=>document.getElementById(id);
let W,H,planets=[],stars=[],running=true,drag=null;
let gravity=1,speed=1,radius=12,last=0;

const colors=['#a78bfa','#5eead4','#60a5fa','#fb7185','#fbbf24','#e879f9'];

function resize(){
 const r=canvas.getBoundingClientRect();
 W=canvas.width=r.width*devicePixelRatio;
 H=canvas.height=r.height*devicePixelRatio;
 ctx.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0);
 W=r.width;H=r.height;
 stars=Array.from({length:130},()=>({
  x:Math.random()*W,y:Math.random()*H,
  r:Math.random()*1.4,alpha:Math.random()
 }));
}
window.addEventListener('resize',resize);
resize();

function addPlanet(x=W/2,y=H/2,star=false){
 const mass=star?1600:radius*radius;
 planets.push({
  x,y,vx:(Math.random()-.5)*1.5,
  vy:(Math.random()-.5)*1.5,
  mass,r:star?23:radius,
  color:star?'#ffe9a6':colors[Math.floor(Math.random()*colors.length)],
  star
 });
 updateCount();
}

function updateCount(){$('count').textContent=planets.length}

function reset(){
 planets=[];
 addPlanet(W/2,H/2,true);
 for(let i=0;i<4;i++){
  const a=i*Math.PI/2;
  const d=85+i*48;
  const x=W/2+Math.cos(a)*d;
  const y=H/2+Math.sin(a)*d;
  addPlanet(x,y);
  const p=planets.at(-1);
  const orbital=Math.sqrt(.5*1600/d);
  p.vx=-Math.sin(a)*orbital;
  p.vy=Math.cos(a)*orbital;
 }
}
reset();

function physics(dt){
 const n=planets.length;
 const ax=Array(n).fill(0),ay=Array(n).fill(0);
 for(let i=0;i<n;i++){
  for(let j=i+1;j<n;j++){
   const a=planets[i],b=planets[j];
   let dx=b.x-a.x,dy=b.y-a.y;
   let d2=dx*dx+dy*dy+100;
   let d=Math.sqrt(d2);
   let f=gravity*0.55/d2;
   ax[i]+=f*b.mass*dx/d;
   ay[i]+=f*b.mass*dy/d;
   ax[j]-=f*a.mass*dx/d;
   ay[j]-=f*a.mass*dy/d;
  }
 }
 for(let i=0;i<n;i++){
  const p=planets[i];
  if(p===drag)continue;
  p.vx+=ax[i]*dt;
  p.vy+=ay[i]*dt;
  p.vx*=.999;
  p.vy*=.999;
  p.x+=p.vx*dt;
  p.y+=p.vy*dt;
 }
}

function draw(){
 ctx.fillStyle='#050711';
 ctx.fillRect(0,0,W,H);

 for(const s of stars){
  ctx.fillStyle=`rgba(210,220,255,${.2+s.alpha*.6})`;
  ctx.beginPath();ctx.arc(s.x,s.y,s.r,0,Math.PI*2);ctx.fill();
 }

 // Draw gravitational connections
 for(let i=0;i<planets.length;i++){
  for(let j=i+1;j<planets.length;j++){
   const a=planets[i],b=planets[j];
   const d=Math.hypot(a.x-b.x,a.y-b.y);
   if(d<190){
    ctx.strokeStyle=`rgba(140,130,255,${.13*(1-d/190)})`;
    ctx.lineWidth=1;
    ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.stroke();
   }
  }
 }

 for(const p of planets){
  const glow=ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,p.r*3);
  glow.addColorStop(0,p.color+'55');
  glow.addColorStop(1,'transparent');
  ctx.fillStyle=glow;
  ctx.beginPath();ctx.arc(p.x,p.y,p.r*3,0,Math.PI*2);ctx.fill();

  const g=ctx.createRadialGradient(p.x-p.r*.3,p.y-p.r*.3,1,p.x,p.y,p.r);
  g.addColorStop(0,'#ffffff');
  g.addColorStop(.25,p.color);
  g.addColorStop(1,'#24233f');
  ctx.fillStyle=g;
  ctx.beginPath();ctx.arc(p.x,p.y,p.r,0,Math.PI*2);ctx.fill();
 }
}

function frame(t){
 const dt=Math.min((t-last)/16.67||1,2)*speed;
 last=t;
 if(running)physics(dt);
 draw();
 requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

function pointer(e){
 const r=canvas.getBoundingClientRect();
 return {x:e.clientX-r.left,y:e.clientY-r.top};
}
canvas.addEventListener('pointerdown',e=>{
 const q=pointer(e);
 drag=[...planets].reverse().find(p=>Math.hypot(p.x-q.x,p.y-q.y)<p.r+8);
 if(drag){canvas.setPointerCapture(e.pointerId)}
 else addPlanet(q.x,q.y);
});
canvas.addEventListener('pointermove',e=>{
 if(drag){
  const q=pointer(e);
  drag.x=q.x;drag.y=q.y;
  drag.vx=0;drag.vy=0;
 }
});
canvas.addEventListener('pointerup',()=>drag=null);
canvas.addEventListener('pointercancel',()=>drag=null);

$('gravity').oninput=e=>{
 gravity=+e.target.value;$('gv').textContent=gravity.toFixed(1)+'x';
};
$('speed').oninput=e=>{
 speed=+e.target.value;$('sv').textContent=speed.toFixed(1)+'x';
};
$('radius').oninput=e=>{
 radius=+e.target.value;$('rv').textContent=radius;
};
$('pause').onclick=()=>{
 running=!running;
 $('pause').textContent=running?'Ⅱ Pause':'▶ Resume';
 $('status').textContent=running?'RUNNING':'PAUSED';
};
$('reset').onclick=reset;
$('add').onclick=()=>addPlanet(W/2+(Math.random()-.5)*100,H/2+(Math.random()-.5)*100);
$('sun').onclick=()=>addPlanet(W/2,H/2,true);
$('clear').onclick=()=>{planets=[];updateCount()};
$('burst').onclick=()=>{
 planets=[];
 for(let i=0;i<12;i++){
  const a=i*Math.PI*2/12;
  addPlanet(W/2+Math.cos(a)*100,H/2+Math.sin(a)*100);
 }
};
</script>
</body>
</html>

Game Source: Gravity Lab - Build Your Solar System

Creator: CosmicLion36

Libraries: none

Complexity: complex (271 lines, 8.0 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: gravity-lab-build-your-solar-system-cosmiclion36" to link back to the original. Then publish at arcadelab.ai/publish.