Custom Code Experiment •
ThreeJS: Fly
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>Insect</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #000; overflow: hidden; height: 100vh; }
#bgCanvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; }
#animationCanvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 2; }
</style>
</head>
<body>
<canvas id="bgCanvas"></canvas>
<canvas id="animationCanvas"></canvas>
<script>
const colors = {
primary: 0x4db8d4,
dark1: 0x0d2a3a,
dark2: 0x0a1e2a,
dark3: 0x000000,
light: 0xaee8f5
};
const bgCanvas = document.getElementById("bgCanvas");
const bgCtx = bgCanvas.getContext("2d");
const pixelSize = 8;
let cols, rows, pixels = [];
function initBackground() {
bgCanvas.width = window.innerWidth;
bgCanvas.height = window.innerHeight;
cols = Math.ceil(bgCanvas.width / pixelSize);
rows = Math.ceil(bgCanvas.height / pixelSize);
pixels = [];
for (let i = 0; i < cols * rows; i++) {
pixels.push({ brightness: Math.random() * 0.3, speed: 0.001 + Math.random() * 0.003 });
}
}
function animateBackground() {
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const pixel = pixels[y * cols + x];
pixel.brightness += pixel.speed;
if (pixel.brightness > 0.4) { pixel.brightness = 0; }
bgCtx.fillStyle = `rgb(${Math.floor(pixel.brightness*10)},${Math.floor(pixel.brightness*30)},${Math.floor(pixel.brightness*50)})`;
bgCtx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
}
}
requestAnimationFrame(animateBackground);
}
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('animationCanvas'), antialias: true, alpha: true });
let scene, camera, insect;
function updateSize() {
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
}
function updateCamera() {
const w = window.innerWidth;
if (w < 480) { camera.position.set(0, 0.5, 7); camera.fov = 60; }
else if (w < 768) { camera.position.set(0, 0.3, 6.5); camera.fov = 55; }
else { camera.position.set(0, 0.3, 5.5); camera.fov = 50; }
camera.updateProjectionMatrix();
}
function mat(color) {
return new THREE.MeshBasicMaterial({ color, wireframe: true });
}
function createInsect() {
const group = new THREE.Group();
const headGeo = new THREE.SphereGeometry(0.22, 24, 24);
const headMesh = new THREE.Mesh(headGeo, mat(colors.dark1));
headMesh.position.set(0, 0.08, 1.35);
headMesh.scale.set(1, 0.95, 1);
group.add(headMesh);
const eyeGeo = new THREE.SphereGeometry(0.18, 20, 20, 0, Math.PI);
[-1, 1].forEach(side => {
const eye = new THREE.Mesh(eyeGeo, mat(colors.light));
eye.position.set(side * 0.2, 0.1, 1.38);
eye.rotation.y = side * Math.PI * 0.35;
eye.scale.set(1, 0.9, 0.7);
group.add(eye);
});
[-1, 1].forEach(side => {
const pts = [];
const segs = 14;
for (let i = 0; i < segs; i++) {
const t = i / (segs - 1);
pts.push(new THREE.Vector3(
side * (0.12 + t * 0.35),
0.18 + t * 0.55 - t * t * 0.15,
1.35 + t * 0.18 - t * t * 0.3
));
}
const curve = new THREE.CatmullRomCurve3(pts);
const antGeo = new THREE.TubeGeometry(curve, 12, 0.013 - 0, 6, false);
const antTaper = new THREE.TubeGeometry(curve, 12, 0.008, 6, false);
group.add(new THREE.Mesh(antTaper, mat(colors.dark1)));
});
const thoraxGeo = new THREE.SphereGeometry(0.3, 28, 28);
const thorax = new THREE.Mesh(thoraxGeo, mat(colors.primary));
thorax.position.set(0, 0.12, 0.72);
thorax.scale.set(0.95, 1.05, 1.5);
group.add(thorax);
const scutGeo = new THREE.SphereGeometry(0.14, 16, 16);
const scut = new THREE.Mesh(scutGeo, mat(colors.primary));
scut.position.set(0, 0.33, 0.55);
scut.scale.set(0.9, 0.7, 1.1);
group.add(scut);
const protoGeo = new THREE.CylinderGeometry(0.12, 0.18, 0.18, 16);
const proto = new THREE.Mesh(protoGeo, mat(colors.dark1));
proto.position.set(0, 0.1, 1.05);
proto.rotation.x = Math.PI * 0.5;
group.add(proto);
const abdSegs = 8;
for (let i = 0; i < abdSegs; i++) {
const t = i / (abdSegs - 1);
const r = 0.17 * (1 - t * 0.72);
const segGeo = new THREE.CylinderGeometry(r * 0.88, r, 0.32, 14, 2);
const isAlt = i % 2 === 0;
const seg = new THREE.Mesh(segGeo, mat(isAlt ? colors.primary : colors.dark1));
const z = -0.12 - i * 0.34;
const y = 0.08 - i * i * 0.012;
seg.position.set(0, y, z);
seg.rotation.x = Math.PI * 0.5 + i * 0.018;
group.add(seg);
if (i > 1 && i < abdSegs - 1) {
[-1, 1].forEach(side => {
const nubGeo = new THREE.SphereGeometry(r * 0.4, 8, 8);
const nub = new THREE.Mesh(nubGeo, mat(colors.light));
nub.position.set(side * r * 1.1, y, z);
group.add(nub);
});
}
}
[-1, 1].forEach(side => {
const cerciPts = [
new THREE.Vector3(side * 0.04, 0.08 - 7*7*0.012, -0.12 - 7*0.34),
new THREE.Vector3(side * 0.1, 0.02 - 7*7*0.012 - 0.04, -0.12 - 7*0.34 - 0.2),
new THREE.Vector3(side * 0.13, -0.1, -2.75)
];
const cerciCurve = new THREE.CatmullRomCurve3(cerciPts);
const cerciGeo = new THREE.TubeGeometry(cerciCurve, 8, 0.018, 5, false);
group.add(new THREE.Mesh(cerciGeo, mat(colors.light)));
});
const legDefs = [
{ side: -1, z: 0.88, az: 0.55, sp: 0.9, drop: -0.18 },
{ side: 1, z: 0.88, az:-0.55, sp: 0.9, drop: -0.18 },
{ side: -1, z: 0.68, az: 0.75, sp: 1.05, drop: -0.22 },
{ side: 1, z: 0.68, az:-0.75, sp: 1.05, drop: -0.22 },
{ side: -1, z: 0.48, az: 0.95, sp: 1.12, drop: -0.26 },
{ side: 1, z: 0.48, az:-0.95, sp: 1.12, drop: -0.26 },
];
legDefs.forEach(def => {
const baseX = def.side * 0.28;
const baseY = 0.0;
const baseZ = def.z;
const coxGeo = new THREE.CylinderGeometry(0.028, 0.032, 0.1, 8);
const cox = new THREE.Mesh(coxGeo, mat(colors.dark1));
cox.position.set(baseX, baseY, baseZ);
cox.rotation.z = def.az;
group.add(cox);
const femX = baseX + def.side * 0.22;
const femY = baseY + def.drop * 0.4;
const femGeo = new THREE.CylinderGeometry(0.028, 0.022, 0.42, 8);
const fem = new THREE.Mesh(femGeo, mat(colors.primary));
fem.position.set(femX, femY, baseZ);
fem.rotation.z = def.az * 0.55;
fem.rotation.y = def.side * 0.25;
group.add(fem);
const tibX = femX + def.side * 0.26 * def.sp;
const tibY = femY + def.drop * 0.7;
const tibGeo = new THREE.CylinderGeometry(0.018, 0.013, 0.46, 7);
const tib = new THREE.Mesh(tibGeo, mat(colors.light));
tib.position.set(tibX, tibY, baseZ + 0.02);
tib.rotation.z = def.az * 0.25 + def.side * 0.6;
tib.rotation.y = def.side * 0.2;
group.add(tib);
for (let t = 0; t < 3; t++) {
const tarX = tibX + def.side * (0.08 + t * 0.06);
const tarY = tibY + def.drop * 0.55 - t * 0.06;
const tarGeo = new THREE.CylinderGeometry(0.011, 0.008, 0.1, 5);
const tar = new THREE.Mesh(tarGeo, mat(colors.dark1));
tar.position.set(tarX, tarY, baseZ + t * 0.02);
tar.rotation.z = def.az * 0.1 + def.side * 0.9;
group.add(tar);
}
});
const wingDefs = [
{ side: 1, fore: true, x: 0.3, y: 0.38, z: 0.75, rx: -0.08, ry: -0.18, rz: 0.05 },
{ side: 1, fore: false, x: 0.32, y: 0.3, z: 0.42, rx: -0.12, ry: -0.28, rz: 0.08 },
{ side: -1, fore: true, x: -0.3, y: 0.38, z: 0.75, rx: -0.08, ry: 0.18, rz: -0.05 },
{ side: -1, fore: false, x: -0.32, y: 0.3, z: 0.42, rx: -0.12, ry: 0.28, rz: -0.08 },
];
wingDefs.forEach(def => {
const len = def.fore ? 1.85 : 1.55;
const wid = def.fore ? 0.42 : 0.38;
const wShape = new THREE.Shape();
wShape.moveTo(0, 0);
wShape.bezierCurveTo(len * 0.25, wid * 0.6, len * 0.55, wid, len * 0.75, wid * 0.85);
wShape.bezierCurveTo(len * 0.9, wid * 0.65, len, wid * 0.2, len, 0);
wShape.bezierCurveTo(len * 0.9, -wid * 0.18, len * 0.5, -wid * 0.25, len * 0.2, -wid * 0.1);
wShape.bezierCurveTo(len * 0.05, -wid * 0.05, 0, 0, 0, 0);
const wGeo = new THREE.ShapeGeometry(wShape, 8);
const wing = new THREE.Mesh(wGeo, new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true, side: THREE.DoubleSide, transparent: true, opacity: 0.85 }));
wing.position.set(def.x, def.y, def.z);
wing.rotation.set(def.rx, def.ry, def.rz);
if (def.side === -1) wing.scale.x = -1;
group.add(wing);
const veinData = [
{ p0: [0,0,0], p1: [len*0.5, 0.02, 0], p2: [len, 0, 0] },
{ p0: [0,0,0], p1: [len*0.4, wid*0.4, 0], p2: [len*0.8, wid*0.6, 0] },
{ p0: [len*0.1,wid*0.08,0], p1: [len*0.5, wid*0.55, 0], p2: [len*0.72, wid*0.78, 0] },
{ p0: [len*0.2, 0.01, 0], p1: [len*0.22, wid*0.25, 0], p2: [len*0.25, wid*0.48, 0] },
{ p0: [len*0.42, 0.01, 0],p1: [len*0.44, wid*0.3, 0], p2: [len*0.47, wid*0.58, 0] },
{ p0: [len*0.62, 0.01, 0],p1: [len*0.63, wid*0.25, 0], p2: [len*0.65, wid*0.45, 0] },
{ p0: [len*0.78,wid*0.05,0],p1:[len*0.88,wid*0.08,0], p2: [len*0.96, wid*0.05, 0] },
];
veinData.forEach(v => {
const vc = new THREE.QuadraticBezierCurve3(
new THREE.Vector3(...v.p0),
new THREE.Vector3(...v.p1),
new THREE.Vector3(...v.p2)
);
const vGeo = new THREE.TubeGeometry(vc, 8, 0.006, 4, false);
const vMesh = new THREE.Mesh(vGeo, mat(colors.primary));
vMesh.position.set(def.x, def.y, def.z);
vMesh.rotation.set(def.rx, def.ry, def.rz);
if (def.side === -1) vMesh.scale.x = -1;
group.add(vMesh);
});
});
for (let i = 0; i < 5; i++) {
[-1, 1].forEach(side => {
const spGeo = new THREE.TorusGeometry(0.025, 0.008, 6, 12);
const sp = new THREE.Mesh(spGeo, mat(colors.light));
sp.position.set(side * 0.19, 0.05 - i * i * 0.01, 0.35 - i * 0.34);
sp.rotation.y = side * Math.PI * 0.4;
group.add(sp);
});
}
return group;
}
function init() {
initBackground();
animateBackground();
updateSize();
scene = new THREE.Scene();
scene.background = new THREE.Color(colors.dark3);
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
updateCamera();
insect = createInsect();
scene.add(insect);
const amb = new THREE.AmbientLight(colors.light, 0.5);
scene.add(amb);
const pl1 = new THREE.PointLight(colors.primary, 1.5, 100);
pl1.position.set(5, 5, 5); scene.add(pl1);
const pl2 = new THREE.PointLight(colors.light, 0.8, 100);
pl2.position.set(-5, -3, 3); scene.add(pl2);
const pl3 = new THREE.PointLight(colors.primary, 0.6, 100);
pl3.position.set(0, 2, -8); scene.add(pl3);
animate();
}
function animate() {
requestAnimationFrame(animate);
const t = Date.now();
if (insect) {
insect.rotation.y += 0.004;
insect.position.y = Math.sin(t * 0.0015) * 0.09;
insect.rotation.x = Math.sin(t * 0.001) * 0.05;
insect.rotation.z = Math.sin(t * 0.0018) * 0.018;
}
renderer.render(scene, camera);
}
window.addEventListener('resize', () => {
updateSize();
initBackground();
if (camera) { camera.aspect = window.innerWidth / window.innerHeight; updateCamera(); }
});
window.addEventListener('load', init);
</script>
</body>
</html>