Custom Code Experiment •
Bees Background
Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Bees Background</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { width: 100vw; height: 100vh; overflow: hidden; background: #0d0700; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const canvas = document.getElementById('c');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.4;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0d0700);
scene.fog = new THREE.FogExp2(0x0d0700, 0.012);
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 300);
camera.position.set(0, 0, 60);
scene.add(new THREE.AmbientLight(0x201000, 0.8));
const keyLight = new THREE.DirectionalLight(0xffe066, 2.5);
keyLight.position.set(20, 40, 30);
scene.add(keyLight);
const fillLight = new THREE.DirectionalLight(0xff9900, 1.0);
fillLight.position.set(-30, 10, 20);
scene.add(fillLight);
const rimLight = new THREE.DirectionalLight(0xffcc00, 0.8);
rimLight.position.set(0, -20, -20);
scene.add(rimLight);
const yellowMat = new THREE.MeshPhongMaterial({ color: 0xffbb00, specular: 0xffee88, shininess: 80, emissive: 0x331100 });
const blackMat = new THREE.MeshPhongMaterial({ color: 0x0a0500, specular: 0x443322, shininess: 40, emissive: 0x000000 });
const darkBrownMat = new THREE.MeshPhongMaterial({ color: 0x2a1400, specular: 0x443322, shininess: 30 });
const eyeMat = new THREE.MeshPhongMaterial({ color: 0x331100, specular: 0xffffff, shininess: 200 });
const stingerMat = new THREE.MeshPhongMaterial({ color: 0x0a0500, shininess: 20 });
const wingMat = new THREE.MeshPhongMaterial({
color: 0xc8eeff, transparent: true, opacity: 0.28,
side: THREE.DoubleSide, shininess: 200, specular: 0xffffff,
emissive: 0x112233
});
const S = 2.0;
function makeBee() {
const g = new THREE.Group();
const abdGeo = new THREE.SphereGeometry(S*0.62, 14, 10);
abdGeo.scale(1.0, 0.72, 1.72);
const abd = new THREE.Mesh(abdGeo, yellowMat.clone());
abd.position.z = -S*0.52;
g.add(abd);
const tipGeo = new THREE.ConeGeometry(S*0.18, S*0.38, 8);
const tip = new THREE.Mesh(tipGeo, stingerMat.clone());
tip.position.z = -S*1.32;
tip.rotation.x = Math.PI/2;
g.add(tip);
const stripeData = [
{ z: -S*0.95, col: 0x0a0500, w: 0.13 },
{ z: -S*0.62, col: 0x0a0500, w: 0.13 },
{ z: -S*0.28, col: 0x0a0500, w: 0.10 },
];
stripeData.forEach(sd => {
const sg = new THREE.TorusGeometry(S*0.52, S*sd.w, 8, 20);
sg.scale(1.0, 0.45, 1.55);
const sm = new THREE.Mesh(sg, new THREE.MeshPhongMaterial({
color: sd.col, specular: 0x221100, shininess: 30, emissive: 0x000000
}));
sm.position.z = sd.z;
sm.rotation.x = Math.PI/2;
g.add(sm);
});
const underGeo = new THREE.SphereGeometry(S*0.61, 14, 6, 0, Math.PI*2, Math.PI/2, Math.PI/2);
underGeo.scale(1.0, 0.72, 1.72);
const underMesh = new THREE.Mesh(underGeo, new THREE.MeshPhongMaterial({
color: 0xcc8800, shininess: 20, side: THREE.BackSide, transparent: true, opacity: 0.5
}));
underMesh.position.z = -S*0.52;
g.add(underMesh);
const petGeo = new THREE.SphereGeometry(S*0.14, 8, 8);
petGeo.scale(1, 0.9, 1.1);
const pet = new THREE.Mesh(petGeo, blackMat.clone());
pet.position.z = S*0.24;
g.add(pet);
const thorGeo = new THREE.SphereGeometry(S*0.46, 14, 10);
thorGeo.scale(1.0, 0.92, 1.05);
const thorax = new THREE.Mesh(thorGeo, darkBrownMat.clone());
thorax.position.z = S*0.62;
g.add(thorax);
const tsg = new THREE.TorusGeometry(S*0.43, S*0.09, 8, 18);
tsg.scale(1.0, 0.5, 0.9);
const thorStripe = new THREE.Mesh(tsg, new THREE.MeshPhongMaterial({ color: 0xffaa00, shininess: 40 }));
thorStripe.position.z = S*0.68;
thorStripe.rotation.x = Math.PI/2;
g.add(thorStripe);
const headGeo = new THREE.SphereGeometry(S*0.36, 12, 10);
headGeo.scale(1.05, 1.0, 0.92);
const head = new THREE.Mesh(headGeo, blackMat.clone());
head.position.z = S*1.18;
g.add(head);
[-1, 1].forEach(side => {
const eyeGeo = new THREE.SphereGeometry(S*0.145, 10, 8);
eyeGeo.scale(0.85, 1.0, 0.6);
const eye = new THREE.Mesh(eyeGeo, new THREE.MeshPhongMaterial({
color: 0x3d1a00, specular: 0xff8800, shininess: 180, emissive: 0x110500
}));
eye.position.set(side * S*0.27, S*0.14, S*1.22);
eye.rotation.y = side * 0.45;
g.add(eye);
const hlGeo = new THREE.SphereGeometry(S*0.055, 6, 6);
const hl = new THREE.Mesh(hlGeo, new THREE.MeshPhongMaterial({
color: 0xffffff, emissive: 0xaaaaaa, shininess: 300
}));
hl.position.set(side * S*0.3, S*0.19, S*1.26);
g.add(hl);
});
[-1,1].forEach(side => {
const mGeo = new THREE.SphereGeometry(S*0.055, 6, 5);
mGeo.scale(0.7, 0.5, 1.2);
const m = new THREE.Mesh(mGeo, blackMat.clone());
m.position.set(side*S*0.13, -S*0.14, S*1.47);
g.add(m);
});
[-1, 1].forEach(side => {
const seg1Geo = new THREE.CylinderGeometry(S*0.028, S*0.028, S*0.45, 6);
const seg1 = new THREE.Mesh(seg1Geo, blackMat.clone());
seg1.position.set(side*S*0.14, S*0.28, S*1.22);
seg1.rotation.z = side * -0.55;
seg1.rotation.x = -0.55;
g.add(seg1);
const elGeo = new THREE.SphereGeometry(S*0.038, 6, 6);
const el = new THREE.Mesh(elGeo, blackMat.clone());
el.position.set(side*S*0.26, S*0.56, S*1.06);
g.add(el);
const seg2Geo = new THREE.CylinderGeometry(S*0.022, S*0.022, S*0.52, 6);
const seg2 = new THREE.Mesh(seg2Geo, blackMat.clone());
seg2.position.set(side*S*0.38, S*0.8, S*0.94);
seg2.rotation.z = side * -0.3;
seg2.rotation.x = -0.85;
g.add(seg2);
const clubGeo = new THREE.SphereGeometry(S*0.065, 7, 7);
const club = new THREE.Mesh(clubGeo, blackMat.clone());
club.position.set(side*S*0.46, S*1.06, S*0.76);
g.add(club);
});
const legPositions = [
{ z: S*0.5, yOff: -S*0.35 },
{ z: S*0.68, yOff: -S*0.38 },
{ z: S*0.82, yOff: -S*0.32 },
];
legPositions.forEach((lp, li) => {
[-1,1].forEach(side => {
const legGeo = new THREE.CylinderGeometry(S*0.022, S*0.018, S*0.55, 5);
const leg = new THREE.Mesh(legGeo, blackMat.clone());
leg.position.set(side*S*0.48, lp.yOff, lp.z);
leg.rotation.z = side * (0.9 + li*0.1);
leg.rotation.x = 0.2;
g.add(leg);
const llGeo = new THREE.CylinderGeometry(S*0.018, S*0.012, S*0.48, 5);
const ll = new THREE.Mesh(llGeo, blackMat.clone());
ll.position.set(side*S*0.75, lp.yOff - S*0.3, lp.z + S*0.1);
ll.rotation.z = side * (1.2 + li*0.08);
ll.rotation.x = 0.35;
g.add(ll);
});
});
const wings = [];
const wingDefs = [
{ scX:1.9, scY:0.75, x:-S*0.55, y:S*0.42, z:S*0.72, rx:-0.25, ry:-0.12, rz:-0.38, side:-1, fore:true },
{ scX:1.9, scY:0.75, x: S*0.55, y:S*0.42, z:S*0.72, rx:-0.25, ry: 0.12, rz: 0.38, side: 1, fore:true },
{ scX:1.35, scY:0.55, x:-S*0.48, y:S*0.28, z:S*0.48, rx:-0.18, ry:-0.08, rz:-0.55, side:-1, fore:false },
{ scX:1.35, scY:0.55, x: S*0.48, y:S*0.28, z:S*0.48, rx:-0.18, ry: 0.08, rz: 0.55, side: 1, fore:false },
];
wingDefs.forEach(wd => {
const wGeo = new THREE.PlaneGeometry(S*wd.scX, S*wd.scY, 6, 4);
const pos = wGeo.attributes.position;
for (let vi = 0; vi < pos.count; vi++) {
const nx = pos.getX(vi) / (S*wd.scX*0.5);
const taper = 1.0 - Math.abs(nx) * 0.35;
pos.setY(vi, pos.getY(vi) * taper);
}
pos.needsUpdate = true;
wGeo.computeVertexNormals();
const wMat = new THREE.MeshPhongMaterial({
color: wd.fore ? 0xd4f0ff : 0xc0e8ff,
transparent: true, opacity: wd.fore ? 0.32 : 0.25,
side: THREE.DoubleSide, shininess: 180, specular: 0xffffff,
emissive: 0x081520
});
const wing = new THREE.Mesh(wGeo, wMat);
wing.position.set(wd.x, wd.y, wd.z);
wing.rotation.set(wd.rx, wd.ry, wd.rz);
g.add(wing);
wings.push({ mesh: wing, baseRZ: wd.rz, side: wd.side });
});
g.userData = {
wings,
phase: Math.random() * Math.PI * 2,
vel: new THREE.Vector3((Math.random()-0.5)*0.12,(Math.random()-0.5)*0.08,(Math.random()-0.5)*0.12),
acc: new THREE.Vector3(),
wobble: Math.random() * Math.PI * 2
};
return g;
}
const NUM = 80, BOUNDS = 45;
const bees = [];
for (let i = 0; i < NUM; i++) {
const b = makeBee();
b.position.set(
(Math.random()-0.5)*BOUNDS*2,
(Math.random()-0.5)*BOUNDS*1.1,
(Math.random()-0.5)*BOUNDS*2
);
b.rotation.y = Math.random() * Math.PI * 2;
scene.add(b);
bees.push(b);
}
const mouseWorld = new THREE.Vector3();
window.addEventListener('mousemove', e => {
mouseWorld.set((e.clientX/innerWidth*2-1)*40,(-(e.clientY/innerHeight)*2+1)*25,0);
});
window.addEventListener('resize', () => {
camera.aspect = innerWidth/innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
const tmp = new THREE.Vector3(), sep = new THREE.Vector3(), ali = new THREE.Vector3(), coh = new THREE.Vector3();
const SEP=5.5, NEI=14, MAXSPD=0.2, MAXF=0.006;
function limit(v,m){ if(v.length()>m) v.setLength(m); }
function flock(bee, i, t) {
const pos = bee.position, vel = bee.userData.vel, acc = bee.userData.acc;
acc.set(0,0,0); sep.set(0,0,0); ali.set(0,0,0); coh.set(0,0,0);
let sc=0, nc=0;
for (let j=0;j<bees.length;j++) {
if(j===i) continue;
const d = pos.distanceTo(bees[j].position);
if(d<SEP&&d>0){ tmp.subVectors(pos,bees[j].position).normalize().divideScalar(d); sep.add(tmp); sc++; }
if(d<NEI){ ali.add(bees[j].userData.vel); coh.add(bees[j].position); nc++; }
}
if(sc>0){ sep.divideScalar(sc); limit(sep,MAXF*2.5); acc.add(sep); }
if(nc>0){
ali.divideScalar(nc); limit(ali,MAXF); acc.add(ali);
coh.divideScalar(nc); tmp.subVectors(coh,pos).setLength(MAXSPD); tmp.sub(vel); limit(tmp,MAXF*0.5); acc.add(tmp);
}
tmp.subVectors(mouseWorld,pos);
if(tmp.length()<35){ tmp.setLength(MAXSPD*0.6); tmp.sub(vel); limit(tmp,0.005); acc.add(tmp); }
acc.y += Math.sin(t*1.1+bee.userData.wobble)*0.0015;
const B=BOUNDS;
if(pos.x>B) pos.x=-B; if(pos.x<-B) pos.x=B;
if(pos.y>B*0.9) pos.y=-B*0.9; if(pos.y<-B*0.9) pos.y=B*0.9;
if(pos.z>B) pos.z=-B; if(pos.z<-B) pos.z=B;
vel.add(acc); limit(vel,MAXSPD); pos.add(vel);
}
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const t = clock.getElapsedTime();
bees.forEach((bee,i) => {
flock(bee,i,t);
if(bee.userData.vel.length()>0.001){
const d = bee.userData.vel.clone().normalize();
bee.rotation.y = Math.atan2(d.x,d.z);
bee.rotation.z = -d.x*0.35;
bee.rotation.x = Math.asin(Math.max(-1,Math.min(1,-d.y)))*0.5;
}
const flap = Math.sin(t*32+bee.userData.phase)*0.75;
bee.userData.wings.forEach(w => {
w.mesh.rotation.z = w.baseRZ + flap*w.side*0.9;
w.mesh.rotation.x = -0.25 + Math.abs(flap)*0.12;
w.mesh.material.opacity = 0.28 + Math.abs(flap)*0.14;
});
});
camera.position.x = Math.sin(t*0.035)*10;
camera.position.y = Math.cos(t*0.028)*5;
camera.lookAt(0,0,0);
renderer.render(scene,camera);
}
animate();
</script>
</body>
</html>