Custom Code Experiment •
ThreeJS: Scorpion 1
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 Scorpion 1</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: 0xe8a020,
dark1: 0x3a1a00,
dark2: 0x1e0d00,
dark3: 0x000000,
light: 0xffd080
};
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*40)},${Math.floor(pixel.brightness*20)},${Math.floor(pixel.brightness*5)})`;
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, scorpion;
let tailSegments = [];
let stingerGroup;
let leftClaw, rightClaw;
let leftClawMovable, rightClawMovable;
let legGroups = [];
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, 9); camera.fov = 60; }
else if (w < 768) { camera.position.set(0, 0, 8); camera.fov = 55; }
else { camera.position.set(0, 0, 7); camera.fov = 50; }
camera.lookAt(0, 0, 0);
camera.updateProjectionMatrix();
}
function mat(color) {
return new THREE.MeshBasicMaterial({ color, wireframe: true });
}
function createScorpion() {
const group = new THREE.Group();
tailSegments = [];
legGroups = [];
const headGeo = new THREE.SphereGeometry(0.28, 20, 20);
const head = new THREE.Mesh(headGeo, mat(colors.dark1));
head.position.set(0, 0.05, 1.5);
head.scale.set(1.2, 0.7, 1.1);
group.add(head);
[[-0.08,0],[0.08,0],[-0.22,0],[0.22,0]].forEach(([ex]) => {
const eGeo = new THREE.SphereGeometry(0.045, 8, 8);
const eye = new THREE.Mesh(eGeo, mat(colors.light));
eye.position.set(ex, 0.12, 1.6);
group.add(eye);
});
[-1,1].forEach(side => {
const pts = [
new THREE.Vector3(side*0.12, 0.0, 1.65),
new THREE.Vector3(side*0.18, -0.05, 1.85),
new THREE.Vector3(side*0.22, -0.08, 2.0)
];
const curve = new THREE.CatmullRomCurve3(pts);
const cGeo = new THREE.TubeGeometry(curve, 6, 0.025, 5, false);
group.add(new THREE.Mesh(cGeo, mat(colors.dark1)));
const tipGeo = new THREE.SphereGeometry(0.04, 6, 6);
const tip = new THREE.Mesh(tipGeo, mat(colors.light));
tip.position.set(side*0.22, -0.08, 2.0);
group.add(tip);
});
[-1,1].forEach(side => {
const armPts = [
new THREE.Vector3(side*0.3, 0.0, 1.4),
new THREE.Vector3(side*0.7, 0.05, 1.55),
new THREE.Vector3(side*1.0, 0.0, 1.7)
];
const armCurve = new THREE.CatmullRomCurve3(armPts);
const armGeo = new THREE.TubeGeometry(armCurve, 8, 0.045, 6, false);
group.add(new THREE.Mesh(armGeo, mat(colors.primary)));
const manusGeo = new THREE.SphereGeometry(0.1, 12, 12);
const manus = new THREE.Mesh(manusGeo, mat(colors.primary));
manus.position.set(side*1.1, 0.0, 1.82);
manus.scale.set(1.0, 0.75, 1.2);
group.add(manus);
const fxPivot = new THREE.Group();
fxPivot.position.set(side*1.1, 0.0, 1.82);
const fxPts = [
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(side*0.05, 0.07, 0.22),
new THREE.Vector3(side*0.0, 0.05, 0.40)
];
const fxCurve = new THREE.CatmullRomCurve3(fxPts);
const fxGeo = new THREE.TubeGeometry(fxCurve, 6, 0.022, 5, false);
fxPivot.add(new THREE.Mesh(fxGeo, mat(colors.dark1)));
group.add(fxPivot);
const mvPivot = new THREE.Group();
mvPivot.position.set(side*1.1, 0.0, 1.82);
const mvPts = [
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(side*0.05, -0.07, 0.22),
new THREE.Vector3(side*0.0, -0.05, 0.40)
];
const mvCurve = new THREE.CatmullRomCurve3(mvPts);
const mvGeo = new THREE.TubeGeometry(mvCurve, 6, 0.020, 5, false);
mvPivot.add(new THREE.Mesh(mvGeo, mat(colors.light)));
group.add(mvPivot);
if (side === -1) { leftClaw = fxPivot; leftClawMovable = mvPivot; }
else { rightClaw = fxPivot; rightClawMovable = mvPivot; }
});
const carapaceGeo = new THREE.SphereGeometry(0.52, 24, 24);
const carapace = new THREE.Mesh(carapaceGeo, mat(colors.primary));
carapace.position.set(0, 0.08, 0.7);
carapace.scale.set(1.1, 0.6, 1.6);
group.add(carapace);
const mesoSegs = 6;
for (let i = 0; i < mesoSegs; i++) {
const t = i / (mesoSegs - 1);
const w = 0.44 - t * 0.12;
const segGeo = new THREE.CylinderGeometry(w * 0.88, w, 0.28, 14, 2);
const isAlt = i % 2 === 0;
const seg = new THREE.Mesh(segGeo, mat(isAlt ? colors.primary : colors.dark1));
const z = -0.1 - i * 0.3;
const y = 0.06 - i * 0.01;
seg.position.set(0, y, z);
seg.rotation.x = Math.PI * 0.5;
group.add(seg);
[-1,1].forEach(side => {
const nubGeo = new THREE.SphereGeometry(w * 0.28, 7, 7);
const nub = new THREE.Mesh(nubGeo, mat(colors.light));
nub.position.set(side * w * 1.05, y, z);
group.add(nub);
});
}
const tailSegsCount = 5;
const tailBaseZ = -0.1 - mesoSegs * 0.3;
const tailBaseY = 0.06 - mesoSegs * 0.01;
let prevPivot = group;
let prevPos = new THREE.Vector3(0, tailBaseY, tailBaseZ);
for (let i = 0; i < tailSegsCount; i++) {
const r = 0.14 - i * 0.018;
const pivot = new THREE.Group();
pivot.position.copy(prevPos);
const isAlt = i % 2 === 0;
const segGeo = new THREE.CylinderGeometry(r * 0.9, r, 0.30, 12, 2);
const seg = new THREE.Mesh(segGeo, mat(isAlt ? colors.dark1 : colors.primary));
seg.position.set(0, 0, -0.15);
seg.rotation.x = Math.PI * 0.5;
pivot.add(seg);
prevPivot.add(pivot);
tailSegments.push(pivot);
prevPivot = pivot;
prevPos = new THREE.Vector3(0, 0, -0.30);
}
stingerGroup = new THREE.Group();
stingerGroup.position.set(0, 0, -0.30);
const bulbGeo = new THREE.SphereGeometry(0.14, 12, 12);
const bulb = new THREE.Mesh(bulbGeo, mat(colors.primary));
bulb.scale.set(0.85, 1.1, 1.0);
stingerGroup.add(bulb);
const stingerPts = [
new THREE.Vector3(0, 0.0, 0),
new THREE.Vector3(0, 0.22, 0.12),
new THREE.Vector3(0, 0.38, 0.28)
];
const stingerCurve = new THREE.CatmullRomCurve3(stingerPts);
const stingerGeo = new THREE.TubeGeometry(stingerCurve, 8, 0.022, 5, false);
stingerGroup.add(new THREE.Mesh(stingerGeo, mat(colors.light)));
const tipGeo2 = new THREE.ConeGeometry(0.022, 0.12, 6);
const stingerTip = new THREE.Mesh(tipGeo2, mat(colors.light));
stingerTip.position.set(0, 0.44, 0.34);
stingerTip.rotation.x = -0.7;
stingerGroup.add(stingerTip);
prevPivot.add(stingerGroup);
const legPairs = [
{ z: 1.15, spread: 1.0 },
{ z: 0.82, spread: 1.05 },
{ z: 0.50, spread: 1.08 },
{ z: 0.18, spread: 1.12 },
];
legPairs.forEach((pair, pi) => {
[-1,1].forEach(side => {
const legGroup = new THREE.Group();
legGroup.position.set(side * 0.48, -0.02, pair.z);
group.add(legGroup);
const coxGeo = new THREE.CylinderGeometry(0.03, 0.035, 0.12, 7);
const cox = new THREE.Mesh(coxGeo, mat(colors.dark1));
cox.rotation.z = side * 1.1;
legGroup.add(cox);
const femGroup = new THREE.Group();
femGroup.position.set(side * 0.18, -0.06, 0);
const femGeo = new THREE.CylinderGeometry(0.025, 0.020, 0.45, 7);
const fem = new THREE.Mesh(femGeo, mat(colors.primary));
fem.rotation.z = side * 0.6;
femGroup.add(fem);
legGroup.add(femGroup);
const tibGroup = new THREE.Group();
tibGroup.position.set(side * 0.32, -0.18, 0);
const tibGeo = new THREE.CylinderGeometry(0.018, 0.013, 0.42, 6);
const tib = new THREE.Mesh(tibGeo, mat(colors.light));
tib.rotation.z = side * 0.9;
tibGroup.add(tib);
legGroup.add(tibGroup);
for (let t = 0; t < 2; t++) {
const tarGeo = new THREE.CylinderGeometry(0.011, 0.008, 0.10, 5);
const tar = new THREE.Mesh(tarGeo, mat(colors.dark1));
tar.position.set(side * (0.42 + t * 0.07), -0.26 - t * 0.06, 0);
tar.rotation.z = side * 0.92;
legGroup.add(tar);
}
const clawGeo = new THREE.ConeGeometry(0.012, 0.07, 5);
const claw = new THREE.Mesh(clawGeo, mat(colors.light));
claw.position.set(side * 0.57, -0.35, 0);
claw.rotation.z = side * -0.4;
legGroup.add(claw);
legGroups.push({ group: legGroup, side, pi });
});
});
[-1,1].forEach(side => {
for (let t = 0; t < 5; t++) {
const torusGeo = new THREE.TorusGeometry(0.018, 0.006, 5, 8);
const pec = new THREE.Mesh(torusGeo, mat(colors.light));
pec.position.set(side * (0.06 + t * 0.07), -0.12, 0.0);
pec.rotation.x = Math.PI * 0.5;
group.add(pec);
}
});
group.position.set(0, 0.6, -0.5);
group.rotation.x = 0.08;
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();
scorpion = createScorpion();
scene.add(scorpion);
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() * 0.001;
if (scorpion) {
scorpion.rotation.y += 0.004;
scorpion.position.y = 0.6 + Math.sin(t * 1.5) * 0.07;
scorpion.rotation.z = Math.sin(t * 1.8) * 0.015;
tailSegments.forEach((seg, i) => {
const phase = i * 0.4;
const base = 0.28 + i * 0.18;
seg.rotation.x = base + Math.sin(t * 1.2 + phase) * 0.06;
});
if (stingerGroup) {
stingerGroup.rotation.x = Math.sin(t * 2.0) * 0.08;
}
const clawAngle = (Math.sin(t * 1.5) * 0.5 + 0.5) * 0.35;
if (leftClawMovable) leftClawMovable.rotation.x = -clawAngle;
if (rightClawMovable) rightClawMovable.rotation.x = -clawAngle;
if (leftClaw) leftClaw.rotation.x = clawAngle * 0.5;
if (rightClaw) rightClaw.rotation.x = clawAngle * 0.5;
legGroups.forEach(({ group: lg, side, pi }) => {
const phase = pi * 0.8 + (side > 0 ? Math.PI : 0);
const swing = Math.sin(t * 2.2 + phase) * 0.12;
lg.rotation.x = swing;
lg.position.y = -0.02 + Math.abs(Math.sin(t * 2.2 + phase)) * 0.04;
});
}
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>