🎮ArcadeLab

CHAOS BOIDS: ABSOLUTE MAYHEM

by EpicViper20
683 lines10.0 KB
▶ Play
<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>CHAOS BOIDS</title><style>
*{box-sizing:border-box}

html,body{
  margin:0;
  width:100%;
  height:100%;
  overflow:hidden;
  background:#000;
  color:white;
  font-family:Arial,sans-serif;
  touch-action:none;
}

#c{
  position:fixed;
  inset:0;
  width:100%;
  height:100%;
}

#hud{
  position:fixed;
  top:12px;
  left:12px;
  right:12px;
  display:flex;
  justify-content:space-between;
  align-items:flex-start;
  pointer-events:none;
  z-index:5;
}

.box{
  background:rgba(0,0,0,.65);
  border:2px solid #fff;
  border-radius:14px;
  padding:10px 14px;
  box-shadow:0 0 20px #fff;
  backdrop-filter:blur(8px);
}

#title{
  font-size:22px;
  font-weight:1000;
  letter-spacing:2px;
  text-shadow:3px 3px 0 #ff00ff;
}

#score{
  font-size:18px;
  font-weight:bold;
}

#event{
  position:fixed;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%) scale(0);
  font-size:clamp(30px,9vw,90px);
  font-weight:1000;
  text-align:center;
  z-index:10;
  pointer-events:none;
  text-shadow:
    5px 5px 0 #ff00ff,
    -5px -5px 0 #00ffff;
}

#controls{
  position:fixed;
  bottom:12px;
  left:50%;
  transform:translateX(-50%);
  width:min(500px,calc(100% - 24px));
  z-index:6;
  display:flex;
  gap:8px;
  flex-wrap:wrap;
  justify-content:center;
}

button{
  flex:1;
  min-width:110px;
  padding:13px;
  border:2px solid white;
  border-radius:12px;
  background:#111;
  color:white;
  font-weight:1000;
  font-size:14px;
  cursor:pointer;
  box-shadow:0 0 12px #00ffff;
}

button:active{
  transform:scale(.9) rotate(-2deg);
}

#nuke{
  background:#ff0055;
  box-shadow:0 0 25px #ff0055;
}

#warning{
  position:fixed;
  bottom:85px;
  left:50%;
  transform:translateX(-50%);
  font-size:12px;
  opacity:.7;
  pointer-events:none;
  text-align:center;
}
</style></head><body><canvas id="c"></canvas>

<div id="hud">
  <div class="box">
    <div id="title">🐦 CHAOS BOIDS</div>
    <div id="mode">NORMAL REALITY</div>
  </div>  <div class="box">
    <div id="score">SCORE: 0</div>
    <div id="combo">COMBO: x1</div>
  </div>
</div><div id="event"></div><div id="warning">
  TAP / DRAG THE SCREEN TO CAUSE ABSOLUTE CHAOS
</div><div id="controls">
  <button onclick="spawnMore()">🐦 MORE BIRDS</button>
  <button onclick="randomize()">🎲 RANDOMIZE</button>
  <button id="nuke" onclick="NUKE()">☢️ NUCLEAR CHAOS</button>
</div><script>
"use strict";

const canvas=document.getElementById("c");
const ctx=canvas.getContext("2d");

let W=innerWidth;
let H=innerHeight;

let dpr=Math.min(devicePixelRatio||1,2);

function resize(){
  W=innerWidth;
  H=innerHeight;
  canvas.width=W*dpr;
  canvas.height=H*dpr;
  canvas.style.width=W+"px";
  canvas.style.height=H+"px";
  ctx.setTransform(dpr,0,0,dpr,0,0);
}

addEventListener("resize",resize);
resize();

let birds=[];
let particles=[];
let shockwaves=[];

let score=0;
let combo=1;
let chaos=1;

let mouse={
  x:W/2,
  y:H/2,
  active:false
};

let gravity={
  x:0,
  y:0
};

let rainbow=0;
let screenShake=0;

const NAMES=[
  "Greg",
  "Bob",
  "The Bird",
  "Mr. Banana",
  "Kevin",
  "WHY",
  "Bird.exe",
  "Unknown Object"
];

function rand(a,b){
  return a+Math.random()*(b-a);
}

function createBird(x=rand(0,W),y=rand(0,H)){

  let a=rand(0,Math.PI*2);
  let speed=rand(1,4);

  birds.push({
    x,
    y,
    vx:Math.cos(a)*speed,
    vy:Math.sin(a)*speed,
    size:rand(4,9),
    hue:rand(0,360),
    name:NAMES[Math.floor(Math.random()*NAMES.length)],
    angry:Math.random()<.08
  });
}

for(let i=0;i<160;i++) createBird();

function particle(x,y){

  let a=rand(0,Math.PI*2);
  let s=rand(2,12);

  particles.push({
    x,y,
    vx:Math.cos(a)*s,
    vy:Math.sin(a)*s,
    life:1,
    size:rand(2,8),
    hue:rand(0,360)
  });
}

function explosion(x,y,count=40){

  for(let i=0;i<count;i++){
    particle(x,y);
  }

  shockwaves.push({
    x,y,
    r:5,
    life:1
  });

  screenShake=Math.max(screenShake,15);
}

function scatter(){

  if(!mouse.active)return;

  for(const b of birds){

    let dx=b.x-mouse.x;
    let dy=b.y-mouse.y;

    let d=Math.sqrt(dx*dx+dy*dy)||1;

    if(d<180){

      let force=(1-d/180)*3;

      b.vx+=dx/d*force;
      b.vy+=dy/d*force;

      b.angry=true;
    }
  }

  explosion(mouse.x,mouse.y,4);

  score+=Math.floor(combo);
  combo+=.01;
}

function updateBirds(){

  for(const b of birds){

    let ax=0;
    let ay=0;

    let neighbours=0;

    for(const o of birds){

      if(b===o)continue;

      let dx=o.x-b.x;
      let dy=o.y-b.y;

      let d=Math.sqrt(dx*dx+dy*dy);

      if(d<70){

        neighbours++;

        // SEPARATION
        if(d<28){
          ax-=dx/(d||1)*.13*chaos;
          ay-=dy/(d||1)*.13*chaos;
        }

        // ALIGNMENT
        ax+=(o.vx-b.vx)*.008;
        ay+=(o.vy-b.vy)*.008;

        // COHESION
        ax+=dx*.0005;
        ay+=dy*.0005;
      }
    }

    // RANDOM CHAOS
    ax+=rand(-.025,.025)*chaos;
    ay+=rand(-.025,.025)*chaos;

    // GRAVITY
    ax+=gravity.x;
    ay+=gravity.y;

    // MOUSE
    if(mouse.active){

      let dx=b.x-mouse.x;
      let dy=b.y-mouse.y;
      let d=Math.sqrt(dx*dx+dy*dy)||1;

      if(d<250){
        ax+=dx/d*(250-d)/400;
        ay+=dy/d*(250-d)/400;
      }
    }

    b.vx+=ax;
    b.vy+=ay;

    let maxSpeed=4+chaos*1.8;

    let speed=Math.sqrt(b.vx*b.vx+b.vy*b.vy);

    if(speed>maxSpeed){

      b.vx=b.vx/speed*maxSpeed;
      b.vy=b.vy/speed*maxSpeed;
    }

    b.x+=b.vx;
    b.y+=b.vy;

    // TELEPORT EDGES

    if(b.x<-30)b.x=W+30;
    if(b.x>W+30)b.x=-30;

    if(b.y<-30)b.y=H+30;
    if(b.y>H+30)b.y=-30;

    b.hue+=chaos*.7;

    if(b.angry && Math.random()<.003){
      b.angry=false;
    }
  }
}

function drawBird(b){

  let angle=Math.atan2(b.vy,b.vx);

  ctx.save();

  ctx.translate(b.x,b.y);
  ctx.rotate(angle);

  let color=
    b.angry
    ? `hsl(${rainbow},100%,60%)`
    : `hsl(${b.hue},100%,65%)`;

  ctx.shadowBlur=18;
  ctx.shadowColor=color;

  ctx.fillStyle=color;

  ctx.beginPath();

  ctx.moveTo(b.size*2.2,0);
  ctx.lineTo(-b.size,-b.size);
  ctx.lineTo(-b.size*.3,0);
  ctx.lineTo(-b.size,b.size);
  ctx.closePath();

  ctx.fill();

  ctx.restore();
}

function draw(){

  ctx.save();

  if(screenShake>0){

    ctx.translate(
      rand(-screenShake,screenShake),
      rand(-screenShake,screenShake)
    );

    screenShake*=.86;
  }

  // CRAZY BACKGROUND

  rainbow+=1;

  let grad=ctx.createRadialGradient(
    W/2,H/2,0,
    W/2,H/2,Math.max(W,H)
  );

  grad.addColorStop(
    0,
    `hsl(${rainbow%360},70%,12%)`
  );

  grad.addColorStop(
    1,
    "#000"
  );

  ctx.fillStyle=grad;
  ctx.fillRect(-20,-20,W+40,H+40);

  // GRID

  ctx.globalAlpha=.12;

  ctx.strokeStyle=`hsl(${(rainbow+180)%360},100%,60%)`;

  for(let x=0;x<W;x+=40){

    ctx.beginPath();
    ctx.moveTo(x,0);
    ctx.lineTo(x,H);
    ctx.stroke();
  }

  for(let y=0;y<H;y+=40){

    ctx.beginPath();
    ctx.moveTo(0,y);
    ctx.lineTo(W,y);
    ctx.stroke();
  }

  ctx.globalAlpha=1;

  for(const b of birds){
    drawBird(b);
  }

  // PARTICLES

  for(const p of particles){

    ctx.fillStyle=`hsl(${p.hue},100%,60%)`;

    ctx.shadowBlur=12;
    ctx.shadowColor=ctx.fillStyle;

    ctx.beginPath();
    ctx.arc(p.x,p.y,p.size,0,Math.PI*2);
    ctx.fill();

    p.x+=p.vx;
    p.y+=p.vy;

    p.vx*=.97;
    p.vy*=.97;

    p.life-=.025;
  }

  particles=particles.filter(p=>p.life>0);

  // SHOCKWAVES

  for(const s of shockwaves){

    ctx.beginPath();

    ctx.arc(
      s.x,
      s.y,
      s.r,
      0,
      Math.PI*2
    );

    ctx.strokeStyle=
      `hsla(${rainbow},100%,60%,${s.life})`;

    ctx.lineWidth=5;

    ctx.stroke();

    s.r+=15;
    s.life-=.025;
  }

  shockwaves=shockwaves.filter(s=>s.life>0);

  ctx.restore();
}

function loop(){

  updateBirds();
  scatter();
  draw();

  document.getElementById("score").textContent=
    "SCORE: "+Math.floor(score);

  document.getElementById("combo").textContent=
    "COMBO: x"+combo.toFixed(1);

  requestAnimationFrame(loop);
}

function showEvent(text){

  const e=document.getElementById("event");

  e.textContent=text;

  e.animate(
    [
      {transform:"translate(-50%,-50%) scale(0) rotate(-20deg)"},
      {transform:"translate(-50%,-50%) scale(1.2) rotate(5deg)"},
      {transform:"translate(-50%,-50%) scale(1) rotate(0deg)"}
    ],
    {
      duration:500,
      easing:"cubic-bezier(.2,1.5,.4,1)"
    }
  );
}

function spawnMore(){

  for(let i=0;i<30;i++){
    createBird(rand(0,W),rand(0,H));
  }

  chaos+=.15;

  explosion(W/2,H/2,30);

  showEvent("MORE BIRDS");
}

function randomize(){

  for(const b of birds){

    b.vx*=rand(-2,2);
    b.vy*=rand(-2,2);
    b.hue=rand(0,360);
    b.size=rand(3,15);
  }

  gravity.x=rand(-.03,.03);
  gravity.y=rand(-.03,.03);

  chaos=rand(.5,4);

  explosion(
    rand(0,W),
    rand(0,H),
    60
  );

  showEvent("REALITY.EXE");
}

function NUKE(){

  showEvent("☢️ NUCLEAR CHAOS ☢️");

  chaos=8;

  screenShake=40;

  for(let i=0;i<12;i++){

    setTimeout(()=>{

      let x=rand(0,W);
      let y=rand(0,H);

      explosion(x,y,80);

      for(const b of birds){

        let dx=b.x-x;
        let dy=b.y-y;
        let d=Math.sqrt(dx*dx+dy*dy)||1;

        b.vx+=dx/d*rand(1,5);
        b.vy+=dy/d*rand(1,5);
      }

    },i*100);
  }

  score+=1000;
  combo+=5;
}

function pointer(x,y){

  mouse.x=x;
  mouse.y=y;
  mouse.active=true;
}

canvas.addEventListener("pointerdown",e=>{
  pointer(e.clientX,e.clientY);
});

canvas.addEventListener("pointermove",e=>{
  if(e.buttons)pointer(e.clientX,e.clientY);
});

canvas.addEventListener("pointerup",()=>{
  mouse.active=false;
});

canvas.addEventListener("pointercancel",()=>{
  mouse.active=false;
});

// RANDOM EVENTS

setInterval(()=>{

  let r=Math.random();

  if(r<.25){

    gravity.x=rand(-.08,.08);
    gravity.y=rand(-.08,.08);

    showEvent("GRAVITY BROKE");

  }else if(r<.5){

    chaos=rand(1,5);

    showEvent("CHAOS +"+chaos.toFixed(1));

  }else if(r<.7){

    for(let i=0;i<10;i++)
      createBird();

    showEvent("BIRD INFLATION");

  }else{

    explosion(
      rand(0,W),
      rand(0,H),
      25
    );
  }

},7000);

loop();

</script></body>
</html>

Game Source: CHAOS BOIDS: ABSOLUTE MAYHEM

Creator: EpicViper20

Libraries: none

Complexity: complex (683 lines, 10.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: chaos-boids-absolute-mayhem-epicviper20" to link back to the original. Then publish at arcadelab.ai/publish.