Custom Code Experiment •
ThreeJS: Experiments
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>ThreeJS: Experiments</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 {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #000000;
color: #e0e0e0;
height: 100vh;
overflow: hidden;
}
.nav-container {
position: absolute;
top: 20px;
left: 20px;
z-index: 20;
display: flex;
flex-direction: column;
gap: 12px;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(8px);
padding: 16px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.nav-container:hover {
background: rgba(0, 0, 0, 0.5);
border-color: rgba(255, 255, 255, 0.12);
}
.nav-btn {
background: transparent;
color: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 14px 20px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
min-width: 140px;
text-align: left;
display: flex;
align-items: center;
gap: 12px;
position: relative;
overflow: hidden;
}
.nav-btn::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, rgba(255, 105, 54, 0.1), transparent);
transform: translateX(-100%);
transition: transform 0.4s ease;
}
.nav-btn:hover {
color: #ffffff;
border-color: rgba(255, 105, 54, 0.3);
background: rgba(255, 105, 54, 0.05);
transform: translateX(4px);
}
.nav-btn:hover::before {
transform: translateX(0);
}
.nav-btn.active {
color: #ffffff;
background: rgba(255, 105, 54, 0.15);
border-color: rgba(255, 105, 54, 0.4);
box-shadow: 0 0 20px rgba(255, 105, 54, 0.2);
}
.nav-btn.active::after {
content: '';
position: absolute;
right: 12px;
width: 6px;
height: 6px;
background: #ff6936;
border-radius: 50%;
animation: pulse 2s infinite;
}
.nav-btn .icon {
font-size: 18px;
opacity: 0.8;
transition: opacity 0.3s ease, transform 0.3s ease;
}
.nav-btn:hover .icon {
opacity: 1;
transform: scale(1.1);
}
.nav-btn.active .icon {
opacity: 1;
color: #ff6936;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(255, 105, 54, 0.7);
}
70% {
box-shadow: 0 0 0 6px rgba(255, 105, 54, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 105, 54, 0);
}
}
.animation-container {
width: 100vw;
height: 100vh;
position: relative;
}
.canvas-wrapper {
width: 100%;
height: 100%;
position: relative;
}
#bgCanvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: #000000;
}
#animationCanvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
z-index: 2;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.5rem;
color: #e0e0e0;
z-index: 100;
background: rgba(0, 0, 0, 0.7);
padding: 20px 40px;
border-radius: 12px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
padding: 12px;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(5px);
border-top: 1px solid rgba(255, 255, 255, 0.1);
font-size: 0.85rem;
opacity: 0.7;
color: #a0a0a0;
z-index: 10;
}
@media (max-width: 768px) {
.nav-container {
top: 10px;
left: 10px;
right: 10px;
flex-direction: row;
justify-content: center;
flex-wrap: wrap;
padding: 12px;
gap: 8px;
}
.nav-btn {
min-width: 80px;
padding: 12px 16px;
font-size: 14px;
justify-content: center;
text-align: center;
}
.nav-btn .text {
display: none;
}
.nav-btn .icon {
font-size: 20px;
}
.nav-btn:hover {
transform: translateY(-2px);
}
footer {
font-size: 0.75rem;
padding: 10px;
}
}
@media (max-width: 480px) {
.nav-container {
padding: 10px;
gap: 6px;
}
.nav-btn {
min-width: 60px;
padding: 10px;
}
.nav-btn .icon {
font-size: 18px;
}
}
</style>
</head>
<body>
<div class="nav-container">
<button class="nav-btn active" id="beeBtn">
<span class="icon">🐝</span>
<span class="text">Bee</span>
</button>
<button class="nav-btn" id="skullBtn">
<span class="icon">💀</span>
<span class="text">Skull</span>
</button>
<button class="nav-btn" id="caveBtn">
<span class="icon">🕳️</span>
<span class="text">Cave</span>
</button>
<button class="nav-btn" id="xenoBtn">
<span class="icon">👽</span>
<span class="text">Alien</span>
</button>
</div>
<div class="animation-container">
<div class="canvas-wrapper">
<div class="loading" id="loadingMessage">Loading Animation...</div>
<canvas id="bgCanvas"></canvas>
<canvas id="animationCanvas"></canvas>
</div>
</div>
<footer>
Created with Three.js | Click buttons to switch animations
</footer>
<script>
let currentAnimation = 'bee';
let scene, camera, renderer, bee, skull, caveStructure, xenomorph;
let animationId;
const colors = {
primary: 0xff6936,
dark1: 0x3b1d22,
dark2: 0x27161e,
dark3: 0x000000,
light: 0xe9e9f0
};
const bgCanvas = document.getElementById("bgCanvas");
const bgCtx = bgCanvas.getContext("2d");
const pixelSize = 8;
let cols, rows;
let 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({
hue: Math.random() * 30,
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 index = y * cols + x;
const pixel = pixels[index];
pixel.brightness += pixel.speed;
if (pixel.brightness > 0.4) {
pixel.brightness = 0;
pixel.hue = Math.random() * 30;
}
const r = Math.floor(0 + pixel.brightness * 50);
const g = Math.floor(0 + pixel.brightness * 20);
const b = Math.floor(0 + pixel.brightness * 10);
bgCtx.fillStyle = `rgb(${r}, ${g}, ${b})`;
bgCtx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
}
}
requestAnimationFrame(animateBackground);
}
function init() {
console.log('Initializing application...');
initBackground();
animateBackground();
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('animationCanvas'),
antialias: true,
alpha: true
});
updateRendererSize();
loadBeeAnimation();
window.addEventListener('resize', onWindowResize);
document.getElementById('beeBtn').addEventListener('click', () => switchAnimation('bee'));
document.getElementById('skullBtn').addEventListener('click', () => switchAnimation('skull'));
document.getElementById('caveBtn').addEventListener('click', () => switchAnimation('cave'));
document.getElementById('xenoBtn').addEventListener('click', () => switchAnimation('xeno'));
setTimeout(() => {
document.getElementById('loadingMessage').style.display = 'none';
}, 1000);
}
function updateRendererSize() {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
}
function switchAnimation(animationType) {
console.log(`Switching to ${animationType} animation`);
if (animationType === currentAnimation) {
console.log('Already on this animation');
return;
}
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
if (scene) {
while(scene.children.length > 0) {
scene.remove(scene.children[0]);
}
}
document.getElementById('beeBtn').classList.toggle('active', animationType === 'bee');
document.getElementById('skullBtn').classList.toggle('active', animationType === 'skull');
document.getElementById('caveBtn').classList.toggle('active', animationType === 'cave');
document.getElementById('xenoBtn').classList.toggle('active', animationType === 'xeno');
if (animationType === 'bee') {
loadBeeAnimation();
} else if (animationType === 'skull') {
loadSkullAnimation();
} else if (animationType === 'cave') {
loadCaveAnimation();
} else {
loadXenomorphAnimation();
}
currentAnimation = animationType;
}
function loadBeeAnimation() {
console.log("Loading detailed bee animation");
scene = new THREE.Scene();
scene.background = new THREE.Color(colors.dark3);
camera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
1000
);
updateBeeCameraPosition();
createDetailedBee();
const ambientLight = new THREE.AmbientLight(colors.light, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(colors.primary, 1.5, 100);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);
const pointLight2 = new THREE.PointLight(colors.light, 0.8, 100);
pointLight2.position.set(-5, -3, 3);
scene.add(pointLight2);
const backLight = new THREE.PointLight(colors.primary, 0.6, 100);
backLight.position.set(0, 2, -8);
scene.add(backLight);
animateBee();
}
function loadSkullAnimation() {
console.log("Loading detailed skull animation");
scene = new THREE.Scene();
scene.background = new THREE.Color(colors.dark3);
camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
if (window.innerWidth < 768) {
camera.position.z = 6;
camera.position.y = 0;
} else {
camera.position.z = 5;
camera.position.y = 0;
}
createDetailedSkull();
const ambientLight = new THREE.AmbientLight(colors.light, 0.3);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(colors.primary, 1, 100);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);
const pointLight2 = new THREE.PointLight(colors.light, 0.5, 100);
pointLight2.position.set(-5, -5, 5);
scene.add(pointLight2);
animateSkull();
}
function loadCaveAnimation() {
console.log("Loading detailed cave animation");
scene = new THREE.Scene();
scene.background = new THREE.Color(colors.dark3);
scene.fog = new THREE.Fog(colors.dark3, 5, 15);
camera = new THREE.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
0.1,
1000
);
updateCaveCameraPosition();
createDetailedCave();
const ambientLight = new THREE.AmbientLight(colors.primary, 0.3);
scene.add(ambientLight);
const caveLight1 = new THREE.PointLight(colors.primary, 1.5, 20);
caveLight1.position.set(3, 2, 2);
scene.add(caveLight1);
const caveLight2 = new THREE.PointLight(colors.light, 0.8, 15);
caveLight2.position.set(-4, -1, -3);
scene.add(caveLight2);
const caveLight3 = new THREE.PointLight(colors.primary, 1.0, 18);
caveLight3.position.set(0, 4, -5);
scene.add(caveLight3);
const rimLight = new THREE.PointLight(colors.light, 0.5, 25);
rimLight.position.set(0, -3, 8);
scene.add(rimLight);
animateCave();
}
function loadXenomorphAnimation() {
console.log("Loading xenomorph animation");
scene = new THREE.Scene();
scene.background = new THREE.Color(colors.dark3);
camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
if (window.innerWidth < 768) {
camera.position.z = 8;
camera.position.y = 0;
} else {
camera.position.z = 6;
camera.position.y = 0;
}
createDetailedXenomorph();
const ambientLight = new THREE.AmbientLight(colors.dark1, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(colors.primary, 2, 100);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);
const pointLight2 = new THREE.PointLight(colors.light, 1.5, 100);
pointLight2.position.set(-5, -3, 3);
scene.add(pointLight2);
const rimLight = new THREE.PointLight(colors.primary, 1.2, 100);
rimLight.position.set(0, 0, -5);
scene.add(rimLight);
animateXenomorph();
}
function updateBeeCameraPosition() {
const aspect = window.innerWidth / window.innerHeight;
if (window.innerWidth < 480) {
camera.position.set(0, 0.5, 5.5);
camera.fov = 60;
} else if (window.innerWidth < 768) {
camera.position.set(0, 0.3, 5);
camera.fov = 55;
} else if (aspect < 1) {
camera.position.set(0, 0.2, 4.5);
camera.fov = 55;
} else {
camera.position.set(0, 0, 4);
camera.fov = 50;
}
camera.updateProjectionMatrix();
}
function createDetailedBee() {
const group = new THREE.Group();
const headGeom = new THREE.SphereGeometry(0.32, 32, 32);
const head = new THREE.Mesh(
headGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
head.position.set(0, 0.15, 1.1);
head.scale.set(1.1, 1.05, 1);
group.add(head);
const eyeGeom = new THREE.SphereGeometry(0.2, 20, 20, 0, Math.PI * 0.9);
const leftEye = new THREE.Mesh(
eyeGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
leftEye.position.set(-0.28, 0.2, 1.2);
leftEye.rotation.y = -Math.PI * 0.25;
leftEye.scale.set(1.15, 1.1, 0.9);
group.add(leftEye);
const rightEye = new THREE.Mesh(
eyeGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
rightEye.position.set(0.28, 0.2, 1.2);
rightEye.rotation.y = Math.PI * 0.25;
rightEye.scale.set(1.15, 1.1, 0.9);
group.add(rightEye);
for (let i = 0; i < 3; i++) {
const ocellusGeom = new THREE.SphereGeometry(0.04, 12, 12);
const ocellus = new THREE.Mesh(
ocellusGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
ocellus.position.set((i - 1) * 0.1, 0.35, 1.15);
group.add(ocellus);
}
for (let side = -1; side <= 1; side += 2) {
const segments = 10;
for (let i = 0; i < segments; i++) {
const segmentGeom = new THREE.CylinderGeometry(
0.018 - i * 0.0012,
0.02 - i * 0.0012,
0.12,
8
);
const segment = new THREE.Mesh(
segmentGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
let xOffset, yOffset, zOffset, rotZ, rotX;
if (i < 2) {
xOffset = side * (0.2 + i * 0.02);
yOffset = 0.32 + i * 0.08;
zOffset = 1.15;
rotZ = side * 0.3;
rotX = -0.2;
} else {
const bendIndex = i - 2;
xOffset = side * (0.24 + bendIndex * 0.05);
yOffset = 0.48 + bendIndex * 0.02;
zOffset = 1.15 + bendIndex * 0.08;
rotZ = side * (0.3 - bendIndex * 0.05);
rotX = -0.2 - bendIndex * 0.1;
}
segment.position.set(xOffset, yOffset, zOffset);
segment.rotation.z = rotZ;
segment.rotation.x = rotX;
group.add(segment);
}
}
const proboscisSegments = 6;
for (let i = 0; i < proboscisSegments; i++) {
const probGeom = new THREE.CylinderGeometry(0.015, 0.012, 0.08, 6);
const prob = new THREE.Mesh(
probGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
const angle = i * 0.3;
prob.position.set(
Math.sin(angle) * 0.12,
0.05 - i * 0.05,
1.15 + Math.cos(angle) * 0.12
);
prob.rotation.z = angle;
prob.rotation.x = Math.PI * 0.5;
group.add(prob);
}
const thoraxMain = new THREE.SphereGeometry(0.35, 28, 28);
const thorax = new THREE.Mesh(
thoraxMain,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
thorax.position.set(0, 0.1, 0.5);
thorax.scale.set(1, 0.95, 1.3);
group.add(thorax);
for (let i = 0; i < 12; i++) {
const fuzzGeom = new THREE.SphereGeometry(0.08, 12, 12);
const fuzz = new THREE.Mesh(
fuzzGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
const angle = (i / 12) * Math.PI * 2;
fuzz.position.set(
Math.cos(angle) * 0.3,
0.1 + Math.sin(i) * 0.15,
0.5 + Math.sin(angle) * 0.3
);
group.add(fuzz);
}
const legConfigs = [
{ side: -1, z: 0.7, angle: 0.5, spread: 0.8 },
{ side: 1, z: 0.7, angle: -0.5, spread: 0.8 },
{ side: -1, z: 0.5, angle: 0.7, spread: 1.0 },
{ side: 1, z: 0.5, angle: -0.7, spread: 1.0 },
{ side: -1, z: 0.3, angle: 0.9, spread: 1.1 },
{ side: 1, z: 0.3, angle: -0.9, spread: 1.1 }
];
legConfigs.forEach((config) => {
const coxaGeom = new THREE.CylinderGeometry(0.04, 0.035, 0.12, 8);
const coxa = new THREE.Mesh(
coxaGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
coxa.position.set(config.side * 0.35, 0.1, config.z);
coxa.rotation.z = config.angle;
group.add(coxa);
const femurGeom = new THREE.CylinderGeometry(0.04, 0.032, 0.35, 8);
const femur = new THREE.Mesh(
femurGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
const femurX = config.side * (0.35 + Math.cos(config.angle) * 0.06);
const femurY = 0.1 + Math.sin(config.angle) * 0.06;
femur.position.set(femurX, femurY, config.z);
femur.rotation.z = config.angle + config.side * 0.5;
femur.rotation.y = config.side * 0.3;
group.add(femur);
const tibiaGeom = new THREE.CylinderGeometry(0.032, 0.024, 0.4, 8);
const tibia = new THREE.Mesh(
tibiaGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
const tibiaX =
config.side *
(0.35 +
Math.cos(config.angle + config.side * 0.5) * 0.23 * config.spread);
const tibiaY = femurY + Math.sin(config.angle + config.side * 0.5) * 0.23;
tibia.position.set(tibiaX, tibiaY, config.z);
tibia.rotation.z = config.angle + config.side * 1.0;
tibia.rotation.y = config.side * 0.3;
group.add(tibia);
if (config.z === 0.3) {
const pollenGeom = new THREE.SphereGeometry(0.06, 12, 12);
const pollen = new THREE.Mesh(
pollenGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
pollen.position.set(tibiaX + config.side * 0.06, tibiaY - 0.1, config.z);
group.add(pollen);
}
for (let t = 0; t < 4; t++) {
const tarsusGeom = new THREE.CylinderGeometry(0.018, 0.015, 0.07, 6);
const tarsus = new THREE.Mesh(
tarsusGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
const tarsusX =
config.side *
(0.35 +
Math.cos(config.angle + config.side * 1.0) * 0.43 * config.spread +
t * 0.04 * config.side);
const tarsusY =
tibiaY + Math.sin(config.angle + config.side * 1.0) * 0.43 - t * 0.035;
tarsus.position.set(tarsusX, tarsusY, config.z + t * 0.03);
tarsus.rotation.z = config.angle + config.side * 1.2;
group.add(tarsus);
}
});
const abdomenSegments = 6;
for (let i = 0; i < abdomenSegments; i++) {
const scale = 0.55 - i * 0.06;
const segGeom = new THREE.SphereGeometry(0.32, 24, 24);
const isYellow = i % 2 === 0;
const segment = new THREE.Mesh(
segGeom,
new THREE.MeshBasicMaterial({
color: isYellow ? colors.primary : colors.dark1,
wireframe: true
})
);
segment.position.set(0, 0.05 - i * 0.02, -0.1 - i * 0.28);
segment.scale.set(scale * 1.1, scale * 0.95, scale * 1.6);
group.add(segment);
}
const stingerGeom = new THREE.CylinderGeometry(0.01, 0.025, 0.25, 8);
const stinger = new THREE.Mesh(
stingerGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
stinger.position.set(0, 0, -1.9);
stinger.rotation.x = Math.PI;
group.add(stinger);
[-1, 1].forEach((side) => {
const frontWingShape = new THREE.Shape();
frontWingShape.moveTo(0, 0);
frontWingShape.bezierCurveTo(0.2, 0.15, 0.5, 0.25, 0.9, 0.28);
frontWingShape.bezierCurveTo(1.2, 0.28, 1.4, 0.2, 1.5, 0.05);
frontWingShape.bezierCurveTo(1.5, -0.15, 1.3, -0.35, 1.0, -0.5);
frontWingShape.bezierCurveTo(0.6, -0.65, 0.3, -0.68, 0.1, -0.6);
frontWingShape.bezierCurveTo(0, -0.4, 0, -0.2, 0, 0);
const frontWingGeom = new THREE.ShapeGeometry(frontWingShape);
const frontWing = new THREE.Mesh(
frontWingGeom,
new THREE.MeshBasicMaterial({
color: colors.light,
wireframe: true,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.9
})
);
frontWing.position.set(side * 0.25, 0.4, 0.45);
frontWing.rotation.x = -0.15;
frontWing.rotation.y = side * 0.3;
frontWing.rotation.z = side * 0.1;
group.add(frontWing);
const veinPositions = [
[0.1, 0, 0.8, -0.2],
[0.3, 0.1, 1.0, -0.1],
[0.5, 0.15, 1.2, -0.2],
[0.2, -0.1, 0.7, -0.4],
[0.4, 0, 0.9, -0.35]
];
veinPositions.forEach((vein) => {
const veinCurve = new THREE.QuadraticBezierCurve3(
new THREE.Vector3(vein[0], 0, vein[1]),
new THREE.Vector3(
(vein[0] + vein[2]) / 2,
0.05,
(vein[1] + vein[3]) / 2
),
new THREE.Vector3(vein[2], 0, vein[3])
);
const veinGeom = new THREE.TubeGeometry(veinCurve, 6, 0.008, 4, false);
const veinMesh = new THREE.Mesh(
veinGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
veinMesh.position.set(side * 0.25, 0.4, 0.45);
veinMesh.rotation.x = -0.15;
veinMesh.rotation.y = side * 0.3;
veinMesh.rotation.z = side * 0.1;
group.add(veinMesh);
});
const hindWingShape = new THREE.Shape();
hindWingShape.moveTo(0, 0);
hindWingShape.bezierCurveTo(0.15, 0.08, 0.35, 0.12, 0.6, 0.1);
hindWingShape.bezierCurveTo(0.75, 0.08, 0.85, 0, 0.85, -0.12);
hindWingShape.bezierCurveTo(0.8, -0.28, 0.6, -0.38, 0.35, -0.4);
hindWingShape.bezierCurveTo(0.15, -0.38, 0.05, -0.25, 0, -0.1);
hindWingShape.lineTo(0, 0);
const hindWingGeom = new THREE.ShapeGeometry(hindWingShape);
const hindWing = new THREE.Mesh(
hindWingGeom,
new THREE.MeshBasicMaterial({
color: colors.light,
wireframe: true,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.8
})
);
hindWing.position.set(side * 0.32, 0.32, 0.15);
hindWing.rotation.x = -0.2;
hindWing.rotation.y = side * 0.5;
hindWing.rotation.z = side * 0.15;
group.add(hindWing);
});
bee = group;
scene.add(bee);
}
function animateBee() {
animationId = requestAnimationFrame(animateBee);
if (bee) {
bee.rotation.y += 0.004;
bee.rotation.x = Math.sin(Date.now() * 0.001) * 0.06;
bee.position.y = Math.sin(Date.now() * 0.0015) * 0.08;
bee.rotation.z = Math.sin(Date.now() * 0.002) * 0.02;
}
renderer.render(scene, camera);
}
function createDetailedSkull() {
const group = new THREE.Group();
const craniumGeom = new THREE.SphereGeometry(
1,
64,
64,
0,
Math.PI * 2,
0,
Math.PI * 0.72
);
const cranium = new THREE.Mesh(
craniumGeom,
new THREE.MeshBasicMaterial({
color: colors.primary,
wireframe: true,
wireframeLinewidth: 1
})
);
cranium.scale.set(1.05, 1.2, 1.28);
cranium.position.y = 0.45;
cranium.position.z = -0.12;
group.add(cranium);
const backSkullGeom = new THREE.SphereGeometry(
0.88,
48,
48,
Math.PI * 0.75,
Math.PI * 0.5,
0,
Math.PI * 0.75
);
const backSkull = new THREE.Mesh(
backSkullGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
backSkull.position.set(0, 0.4, -0.85);
backSkull.scale.set(1.02, 1.15, 1.05);
group.add(backSkull);
const upperFaceGeom = new THREE.SphereGeometry(
0.78,
40,
40,
0,
Math.PI * 2,
Math.PI * 0.42,
Math.PI * 0.38
);
const upperFace = new THREE.Mesh(
upperFaceGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
upperFace.position.set(0, 0.05, 0.68);
upperFace.scale.set(1.08, 0.95, 1.25);
group.add(upperFace);
const jawCurve = new THREE.Shape();
jawCurve.moveTo(-0.58, 0);
jawCurve.bezierCurveTo(-0.62, -0.15, -0.6, -0.3, -0.48, -0.42);
jawCurve.bezierCurveTo(-0.3, -0.52, -0.15, -0.56, 0, -0.58);
jawCurve.bezierCurveTo(0.15, -0.56, 0.3, -0.52, 0.48, -0.42);
jawCurve.bezierCurveTo(0.6, -0.3, 0.62, -0.15, 0.58, 0);
const jawGeom = new THREE.ExtrudeGeometry(jawCurve, {
depth: 0.28,
bevelEnabled: true,
bevelThickness: 0.02,
bevelSize: 0.02,
bevelSegments: 3
});
const jaw = new THREE.Mesh(
jawGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
jaw.position.set(0, -0.72, 0.32);
jaw.rotation.x = Math.PI * 0.08;
group.add(jaw);
const ramusGeom = new THREE.BoxGeometry(0.14, 0.65, 0.32, 6, 10, 6);
const leftRamus = new THREE.Mesh(
ramusGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
leftRamus.position.set(-0.6, -0.38, -0.08);
leftRamus.rotation.z = -0.12;
leftRamus.rotation.x = -0.1;
group.add(leftRamus);
const rightRamus = new THREE.Mesh(
ramusGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
rightRamus.position.set(0.6, -0.38, -0.08);
rightRamus.rotation.z = 0.12;
rightRamus.rotation.x = -0.1;
group.add(rightRamus);
const eyeGeom = new THREE.SphereGeometry(0.24, 24, 24);
const leftEye = new THREE.Mesh(
eyeGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
leftEye.position.set(-0.33, 0.28, 0.82);
leftEye.scale.set(1.15, 1.08, 0.65);
group.add(leftEye);
const rightEye = new THREE.Mesh(
eyeGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
rightEye.position.set(0.33, 0.28, 0.82);
rightEye.scale.set(1.15, 1.08, 0.65);
group.add(rightEye);
const noseShape = new THREE.Shape();
noseShape.moveTo(0, 0.18);
noseShape.bezierCurveTo(-0.08, 0.16, -0.13, 0.08, -0.13, 0);
noseShape.bezierCurveTo(-0.13, -0.12, -0.11, -0.2, -0.09, -0.24);
noseShape.lineTo(0, -0.28);
noseShape.lineTo(0.09, -0.24);
noseShape.bezierCurveTo(0.11, -0.2, 0.13, -0.12, 0.13, 0);
noseShape.bezierCurveTo(0.13, 0.08, 0.08, 0.16, 0, 0.18);
const noseGeom = new THREE.ExtrudeGeometry(noseShape, {
depth: 0.15,
bevelEnabled: true,
bevelThickness: 0.01,
bevelSize: 0.01,
bevelSegments: 2
});
const nose = new THREE.Mesh(
noseGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
nose.position.set(0, 0.02, 0.88);
group.add(nose);
const zygomaticGeom = new THREE.SphereGeometry(
0.2,
20,
20,
0,
Math.PI * 0.85
);
const leftZygomatic = new THREE.Mesh(
zygomaticGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
leftZygomatic.position.set(-0.56, 0.12, 0.58);
leftZygomatic.rotation.y = -Math.PI * 0.28;
leftZygomatic.scale.set(1.25, 0.85, 1.05);
group.add(leftZygomatic);
const rightZygomatic = new THREE.Mesh(
zygomaticGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
rightZygomatic.position.set(0.56, 0.12, 0.58);
rightZygomatic.rotation.y = Math.PI * 1.28;
rightZygomatic.scale.set(1.25, 0.85, 1.05);
group.add(rightZygomatic);
const archCurve = new THREE.EllipseCurve(
0,
0,
0.22,
0.18,
0,
Math.PI * 0.65,
false,
0
);
const archPoints = archCurve.getPoints(16);
const archGeom = new THREE.TubeGeometry(
new THREE.CatmullRomCurve3(
archPoints.map((p) => new THREE.Vector3(p.x, p.y, 0))
),
16,
0.045,
8,
false
);
const leftArch = new THREE.Mesh(
archGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
leftArch.position.set(-0.68, 0.08, 0.12);
leftArch.rotation.x = Math.PI * 0.5;
leftArch.rotation.z = Math.PI * 0.18;
group.add(leftArch);
const rightArch = new THREE.Mesh(
archGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
rightArch.position.set(0.68, 0.08, 0.12);
rightArch.rotation.x = Math.PI * 0.5;
rightArch.rotation.z = -Math.PI * 0.18;
group.add(rightArch);
const browGeom = new THREE.CylinderGeometry(0.04, 0.05, 0.35, 12);
const leftBrow = new THREE.Mesh(
browGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
leftBrow.position.set(-0.25, 0.4, 0.85);
leftBrow.rotation.z = Math.PI * 0.5;
group.add(leftBrow);
const rightBrow = new THREE.Mesh(
browGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
rightBrow.position.set(0.25, 0.4, 0.85);
rightBrow.rotation.z = Math.PI * 0.5;
group.add(rightBrow);
const temporalGeom = new THREE.SphereGeometry(0.25, 16, 16, 0, Math.PI * 0.5);
const leftTemporal = new THREE.Mesh(
temporalGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
leftTemporal.position.set(-0.85, 0.15, -0.1);
leftTemporal.rotation.y = Math.PI * 0.5;
leftTemporal.scale.set(1, 1.2, 0.8);
group.add(leftTemporal);
const rightTemporal = new THREE.Mesh(
temporalGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
rightTemporal.position.set(0.85, 0.15, -0.1);
rightTemporal.rotation.y = -Math.PI * 0.5;
rightTemporal.scale.set(1, 1.2, 0.8);
group.add(rightTemporal);
for (let i = 0; i < 12; i++) {
const toothGeom = new THREE.BoxGeometry(0.055, 0.12, 0.055, 2, 2, 2);
const tooth = new THREE.Mesh(
toothGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
const angle = (i - 5.5) * 0.18;
const radius = 0.5;
tooth.position.set(
Math.sin(angle) * radius,
-0.35,
Math.cos(angle) * radius + 0.45
);
tooth.rotation.y = angle;
group.add(tooth);
}
for (let i = 0; i < 12; i++) {
const toothGeom = new THREE.BoxGeometry(0.055, 0.12, 0.055, 2, 2, 2);
const tooth = new THREE.Mesh(
toothGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
const angle = (i - 5.5) * 0.18;
const radius = 0.48;
tooth.position.set(
Math.sin(angle) * radius,
-0.85,
Math.cos(angle) * radius + 0.42
);
tooth.rotation.y = angle;
group.add(tooth);
}
const foramenGeom = new THREE.TorusGeometry(0.2, 0.05, 12, 20);
const foramen = new THREE.Mesh(
foramenGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
foramen.position.set(0, -0.5, -0.7);
foramen.rotation.x = Math.PI * 0.5;
group.add(foramen);
skull = group;
scene.add(skull);
}
function animateSkull() {
animationId = requestAnimationFrame(animateSkull);
if (skull) {
skull.rotation.y += 0.003;
skull.rotation.x = Math.sin(Date.now() * 0.0005) * 0.1;
}
renderer.render(scene, camera);
}
function updateCaveCameraPosition() {
const aspect = window.innerWidth / window.innerHeight;
if (window.innerWidth < 480) {
camera.position.set(0, 0.5, 4.5);
camera.fov = 65;
} else if (window.innerWidth < 768) {
camera.position.set(0, 0.3, 4);
camera.fov = 62;
} else if (aspect < 1) {
camera.position.set(0, 0.3, 4);
camera.fov = 62;
} else {
camera.position.set(0, 0.2, 3.5);
camera.fov = 60;
}
camera.updateProjectionMatrix();
}
function createDetailedCave() {
const group = new THREE.Group();
const caveTopGeom = new THREE.SphereGeometry(
1.8,
40,
40,
0,
Math.PI * 2,
0,
Math.PI * 0.5
);
const caveTop = new THREE.Mesh(
caveTopGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
caveTop.position.y = 0.8;
caveTop.scale.set(1.3, 1.1, 1.5);
group.add(caveTop);
const caveFloorGeom = new THREE.SphereGeometry(
1.6,
36,
36,
0,
Math.PI * 2,
Math.PI * 0.5,
Math.PI * 0.5
);
const caveFloor = new THREE.Mesh(
caveFloorGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
caveFloor.position.y = -0.8;
caveFloor.scale.set(1.4, 0.6, 1.6);
group.add(caveFloor);
const wallSegments = 12;
for (let i = 0; i < wallSegments; i++) {
const angle = (i / wallSegments) * Math.PI * 2;
const wallGeom = new THREE.PlaneGeometry(0.8, 2.2, 8, 12);
const wall = new THREE.Mesh(
wallGeom,
new THREE.MeshBasicMaterial({
color: i % 2 === 0 ? colors.primary : colors.light,
wireframe: true,
side: THREE.DoubleSide
})
);
wall.position.set(Math.cos(angle) * 1.8, 0, Math.sin(angle) * 2.1);
wall.rotation.y = -angle;
group.add(wall);
}
const stalactitePositions = [
{ x: 0, z: 0, size: 0.18, height: 1.2 },
{ x: -0.8, z: 0.6, size: 0.14, height: 0.9 },
{ x: 0.8, z: 0.6, size: 0.14, height: 0.9 },
{ x: -0.6, z: -0.8, size: 0.12, height: 0.7 },
{ x: 0.6, z: -0.8, size: 0.12, height: 0.7 },
{ x: 0, z: 1.2, size: 0.15, height: 0.8 }
];
stalactitePositions.forEach((pos) => {
const stalactiteGeom = new THREE.ConeGeometry(pos.size, pos.height, 10, 6);
const stalactite = new THREE.Mesh(
stalactiteGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
stalactite.position.set(pos.x, 1.8 - pos.height / 2, pos.z);
group.add(stalactite);
});
const stalagmitePositions = [
{ x: -0.7, z: 0.4, size: 0.16, height: 0.8 },
{ x: 0.7, z: 0.4, size: 0.16, height: 0.8 },
{ x: -0.5, z: -0.6, size: 0.14, height: 0.6 },
{ x: 0.5, z: -0.6, size: 0.14, height: 0.6 },
{ x: 0, z: 1.0, size: 0.18, height: 0.9 }
];
stalagmitePositions.forEach((pos) => {
const stalagmiteGeom = new THREE.ConeGeometry(pos.size, pos.height, 10, 6);
const stalagmite = new THREE.Mesh(
stalagmiteGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
stalagmite.position.set(pos.x, -1.3 + pos.height / 2, pos.z);
stalagmite.rotation.x = Math.PI;
group.add(stalagmite);
});
const entranceTunnelSegments = 5;
for (let i = 0; i < entranceTunnelSegments; i++) {
const depth = 2.2 + i * 0.5;
const tunnelRadius = 0.7 - i * 0.08;
const tunnelGeom = new THREE.TorusGeometry(tunnelRadius, 0.12, 16, 24);
const tunnelRing = new THREE.Mesh(
tunnelGeom,
new THREE.MeshBasicMaterial({
color: colors.light,
wireframe: true
})
);
tunnelRing.position.set(0, 0.1, -depth);
tunnelRing.rotation.y = Math.PI / 2;
group.add(tunnelRing);
}
const leftPassageSegments = 4;
for (let i = 0; i < leftPassageSegments; i++) {
const depth = 1.8 + i * 0.45;
const passageRadius = 0.45 - i * 0.06;
const passageGeom = new THREE.TorusGeometry(passageRadius, 0.08, 12, 20);
const passage = new THREE.Mesh(
passageGeom,
new THREE.MeshBasicMaterial({
color: colors.dark1,
wireframe: true
})
);
passage.position.set(-depth * 0.8, -0.2, -depth * 0.4);
passage.rotation.y = Math.PI * 0.7;
passage.rotation.z = -0.2;
group.add(passage);
}
const rightPassageSegments = 4;
for (let i = 0; i < rightPassageSegments; i++) {
const depth = 1.8 + i * 0.45;
const passageRadius = 0.45 - i * 0.06;
const passageGeom = new THREE.TorusGeometry(passageRadius, 0.08, 12, 20);
const passage = new THREE.Mesh(
passageGeom,
new THREE.MeshBasicMaterial({
color: colors.dark1,
wireframe: true
})
);
passage.position.set(depth * 0.8, -0.2, -depth * 0.4);
passage.rotation.y = -Math.PI * 0.7;
passage.rotation.z = 0.2;
group.add(passage);
}
const columnPositions = [
{ x: -1.2, z: 0, height: 2.8 },
{ x: 1.2, z: 0, height: 2.8 },
{ x: -0.9, z: 1.4, height: 2.5 },
{ x: 0.9, z: 1.4, height: 2.5 }
];
columnPositions.forEach((pos) => {
const columnGeom = new THREE.CylinderGeometry(
0.18,
0.22,
pos.height,
12,
8
);
const column = new THREE.Mesh(
columnGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
column.position.set(pos.x, -0.1, pos.z);
group.add(column);
const capitalGeom = new THREE.CylinderGeometry(0.25, 0.18, 0.15, 12);
const capital = new THREE.Mesh(
capitalGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
capital.position.set(pos.x, pos.height / 2 - 0.1, pos.z);
group.add(capital);
});
const crystalPositions = [
{ x: -1.0, y: 0.4, z: 0.8, size: 0.2 },
{ x: 1.0, y: 0.4, z: 0.8, size: 0.2 },
{ x: -0.5, y: -0.3, z: 1.5, size: 0.15 },
{ x: 0.5, y: -0.3, z: 1.5, size: 0.15 },
{ x: 0, y: 0.6, z: 0.5, size: 0.18 }
];
crystalPositions.forEach((pos) => {
const crystalGeom = new THREE.OctahedronGeometry(pos.size, 1);
const crystal = new THREE.Mesh(
crystalGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
crystal.position.set(pos.x, pos.y, pos.z);
crystal.rotation.y = Math.random() * Math.PI;
group.add(crystal);
});
const floorRings = 4;
for (let i = 1; i <= floorRings; i++) {
const ringGeom = new THREE.TorusGeometry(i * 0.3, 0.03, 8, 24);
const ring = new THREE.Mesh(
ringGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
ring.position.y = -1.3;
ring.rotation.x = Math.PI / 2;
group.add(ring);
}
const archCount = 8;
for (let i = 0; i < archCount; i++) {
const angle = (i / archCount) * Math.PI * 2;
const archCurve = new THREE.QuadraticBezierCurve3(
new THREE.Vector3(Math.cos(angle) * 1.6, -0.5, Math.sin(angle) * 1.8),
new THREE.Vector3(Math.cos(angle) * 0.8, 1.5, Math.sin(angle) * 0.9),
new THREE.Vector3(0, 1.8, 0)
);
const archGeom = new THREE.TubeGeometry(archCurve, 16, 0.06, 8, false);
const arch = new THREE.Mesh(
archGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
group.add(arch);
}
const poolGeom = new THREE.CylinderGeometry(0.6, 0.5, 0.08, 24);
const pool = new THREE.Mesh(
poolGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
pool.position.set(0, -1.32, 0.8);
group.add(pool);
const veinPaths = [
{ start: [-1.5, 0.5, 1], mid: [-0.8, 1.2, 0.5], end: [0, 1.6, 0] },
{ start: [1.5, 0.5, 1], mid: [0.8, 1.2, 0.5], end: [0, 1.6, 0] },
{ start: [-1.2, -0.8, 0], mid: [-0.5, 0, 0.8], end: [0, 0.5, 1.2] }
];
veinPaths.forEach((path) => {
const veinCurve = new THREE.QuadraticBezierCurve3(
new THREE.Vector3(...path.start),
new THREE.Vector3(...path.mid),
new THREE.Vector3(...path.end)
);
const veinGeom = new THREE.TubeGeometry(veinCurve, 12, 0.025, 6, false);
const vein = new THREE.Mesh(
veinGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
group.add(vein);
});
caveStructure = group;
scene.add(caveStructure);
}
function animateCave() {
animationId = requestAnimationFrame(animateCave);
if (caveStructure) {
caveStructure.rotation.y += 0.002;
caveStructure.rotation.x = Math.sin(Date.now() * 0.0003) * 0.05;
caveStructure.scale.set(
1 + Math.sin(Date.now() * 0.0008) * 0.01,
1 + Math.cos(Date.now() * 0.0008) * 0.01,
1 + Math.sin(Date.now() * 0.0008) * 0.01
);
}
renderer.render(scene, camera);
}
function createDetailedXenomorph() {
const group = new THREE.Group();
const craniumGeom = new THREE.SphereGeometry(0.7, 48, 48, 0, Math.PI * 2, 0, Math.PI * 0.65);
const cranium = new THREE.Mesh(
craniumGeom,
new THREE.MeshBasicMaterial({
color: colors.primary,
wireframe: true,
wireframeLinewidth: 1
})
);
cranium.scale.set(1.2, 1, 3.5);
cranium.position.set(0, 0.3, -0.5);
cranium.rotation.x = -0.15;
group.add(cranium);
const backHeadGeom = new THREE.CylinderGeometry(0.55, 0.65, 2.5, 32, 16);
const backHead = new THREE.Mesh(
backHeadGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
backHead.position.set(0, 0.3, -2);
backHead.rotation.x = Math.PI * 0.5;
backHead.scale.set(1.1, 1, 0.85);
group.add(backHead);
for (let i = 0; i < 8; i++) {
const ridgeGeom = new THREE.TorusGeometry(0.6 + i * 0.03, 0.03, 8, 24);
const ridge = new THREE.Mesh(
ridgeGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
ridge.position.set(0, 0.3, -0.8 - i * 0.28);
ridge.rotation.x = Math.PI * 0.5;
ridge.scale.x = 1.1;
ridge.scale.y = 0.85;
group.add(ridge);
}
const faceGeom = new THREE.SphereGeometry(0.6, 32, 32, 0, Math.PI * 2, Math.PI * 0.3, Math.PI * 0.4);
const face = new THREE.Mesh(
faceGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
face.position.set(0, 0, 0.8);
face.scale.set(1.1, 0.95, 1);
group.add(face);
const eyeSocketGeom = new THREE.SphereGeometry(0.15, 16, 16);
const leftEyeSocket = new THREE.Mesh(
eyeSocketGeom,
new THREE.MeshBasicMaterial({ color: colors.dark3, wireframe: true })
);
leftEyeSocket.position.set(-0.35, 0.15, 0.65);
leftEyeSocket.scale.set(1.3, 0.9, 0.6);
group.add(leftEyeSocket);
const rightEyeSocket = new THREE.Mesh(
eyeSocketGeom,
new THREE.MeshBasicMaterial({ color: colors.dark3, wireframe: true })
);
rightEyeSocket.position.set(0.35, 0.15, 0.65);
rightEyeSocket.scale.set(1.3, 0.9, 0.6);
group.add(rightEyeSocket);
const jawGeom = new THREE.SphereGeometry(0.5, 24, 24, 0, Math.PI * 2, Math.PI * 0.5, Math.PI * 0.5);
const jaw = new THREE.Mesh(
jawGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
jaw.position.set(0, -0.4, 0.6);
jaw.scale.set(1, 0.7, 1.2);
group.add(jaw);
const innerJawBase = new THREE.CylinderGeometry(0.08, 0.1, 0.3, 16);
const innerJaw = new THREE.Mesh(
innerJawBase,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
innerJaw.position.set(0, -0.2, 0.9);
innerJaw.rotation.x = Math.PI * 0.4;
group.add(innerJaw);
const innerToothGeom = new THREE.ConeGeometry(0.04, 0.12, 8);
for (let i = 0; i < 4; i++) {
const tooth = new THREE.Mesh(
innerToothGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
const angle = (i - 1.5) * 0.3;
tooth.position.set(Math.sin(angle) * 0.08, -0.3, 0.95 + Math.cos(angle) * 0.05);
tooth.rotation.x = Math.PI;
group.add(tooth);
}
for (let i = 0; i < 16; i++) {
const toothGeom = new THREE.ConeGeometry(0.04, 0.25, 8);
const tooth = new THREE.Mesh(
toothGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
const angle = (i - 7.5) * 0.22;
const radius = 0.52;
tooth.position.set(Math.sin(angle) * radius, -0.45, Math.cos(angle) * radius + 0.6);
tooth.rotation.x = Math.PI;
tooth.rotation.y = angle;
group.add(tooth);
}
const mandibleGeom = new THREE.CylinderGeometry(0.08, 0.1, 0.6, 12);
const leftMandible = new THREE.Mesh(
mandibleGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
leftMandible.position.set(-0.45, -0.5, 0.3);
leftMandible.rotation.z = -0.3;
leftMandible.rotation.x = -0.2;
group.add(leftMandible);
const rightMandible = new THREE.Mesh(
mandibleGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
rightMandible.position.set(0.45, -0.5, 0.3);
rightMandible.rotation.z = 0.3;
rightMandible.rotation.x = -0.2;
group.add(rightMandible);
const tubeGeom = new THREE.CylinderGeometry(0.06, 0.08, 1.2, 12);
const leftTube = new THREE.Mesh(
tubeGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
leftTube.position.set(-0.7, 0.1, -0.5);
leftTube.rotation.z = -0.4;
leftTube.rotation.x = Math.PI * 0.5;
group.add(leftTube);
const rightTube = new THREE.Mesh(
tubeGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
rightTube.position.set(0.7, 0.1, -0.5);
rightTube.rotation.z = 0.4;
rightTube.rotation.x = Math.PI * 0.5;
group.add(rightTube);
const segmentGeom = new THREE.SphereGeometry(0.3, 16, 16, 0, Math.PI * 0.6);
for (let i = 0; i < 3; i++) {
const segment = new THREE.Mesh(
segmentGeom,
new THREE.MeshBasicMaterial({ color: colors.primary, wireframe: true })
);
segment.position.set(0, 0.5 - i * 0.15, 0.4 - i * 0.3);
segment.scale.set(1.3 - i * 0.1, 1, 1);
group.add(segment);
}
const neckGeom = new THREE.CylinderGeometry(0.4, 0.5, 0.8, 24);
const neck = new THREE.Mesh(
neckGeom,
new THREE.MeshBasicMaterial({ color: colors.dark1, wireframe: true })
);
neck.position.set(0, -0.9, 0);
neck.scale.set(1.2, 1, 1);
group.add(neck);
for (let i = 0; i < 5; i++) {
const vertebraGeom = new THREE.TorusGeometry(0.15, 0.04, 8, 16);
const vertebra = new THREE.Mesh(
vertebraGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
vertebra.position.set(0, 0.4 - i * 0.12, -1.5 - i * 0.2);
vertebra.rotation.x = Math.PI * 0.5;
group.add(vertebra);
}
const spineGeom = new THREE.ConeGeometry(0.08, 0.4, 8);
for (let i = 0; i < 4; i++) {
const leftSpine = new THREE.Mesh(
spineGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
leftSpine.position.set(-0.8, 0.3 - i * 0.15, -0.5 - i * 0.3);
leftSpine.rotation.z = -Math.PI * 0.3;
leftSpine.rotation.y = -Math.PI * 0.2;
group.add(leftSpine);
const rightSpine = new THREE.Mesh(
spineGeom,
new THREE.MeshBasicMaterial({ color: colors.light, wireframe: true })
);
rightSpine.position.set(0.8, 0.3 - i * 0.15, -0.5 - i * 0.3);
rightSpine.rotation.z = Math.PI * 0.3;
rightSpine.rotation.y = Math.PI * 0.2;
group.add(rightSpine);
}
xenomorph = group;
scene.add(xenomorph);
}
function animateXenomorph() {
animationId = requestAnimationFrame(animateXenomorph);
if (xenomorph) {
xenomorph.rotation.y += 0.004;
xenomorph.rotation.x = Math.sin(Date.now() * 0.0003) * 0.08;
xenomorph.scale.y = 1 + Math.sin(Date.now() * 0.001) * 0.02;
}
renderer.render(scene, camera);
}
// Window resize handler
function onWindowResize() {
updateRendererSize();
initBackground();
if (camera) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
if (currentAnimation === 'bee') {
updateBeeCameraPosition();
} else if (currentAnimation === 'skull') {
if (window.innerWidth < 768) {
camera.position.z = 6;
camera.position.y = 0;
} else {
camera.position.z = 5;
camera.position.y = 0;
}
} else if (currentAnimation === 'cave') {
updateCaveCameraPosition();
} else {
if (window.innerWidth < 768) {
camera.position.z = 8;
camera.position.y = 0;
} else {
camera.position.z = 6;
camera.position.y = 0;
}
}
}
}
// Initialize when page loads
window.addEventListener('load', init);
</script>
</body>
</html>