Custom Code Experiment •
ThreeJS: Butterfly 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, user-scalable=no">
<title>ThreeJS: Butterfly 1</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body, html {
width: 100%;
height: 100%;
overflow: hidden;
background: #000000;
}
#bfly-wrap {
position: relative;
width: 100vw;
height: 100vh;
background: #000000;
overflow: hidden;
}
#animC {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
z-index: 2;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
<div id="bfly-wrap">
<canvas id="animC"></canvas>
</div>
<script>
(function() {
const colors = {
wing1: 0x5bcaaa,
wing2: 0x1d9e75,
body: 0x9fe1cb,
vein: 0xe1f5ee,
dark: 0x04342c,
spot: 0xfac775
};
const canvasEl = document.getElementById('animC');
const renderer = new THREE.WebGLRenderer({
canvas: canvasEl,
antialias: true,
alpha: false
});
let scene, camera, butterfly;
let leftUpper, rightUpper;
function updateSize() {
const wrap = document.getElementById('bfly-wrap');
const width = wrap.offsetWidth;
const height = wrap.offsetHeight;
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
if (camera) {
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function wireframeMaterial(color) {
return new THREE.MeshBasicMaterial({
color: color,
wireframe: true,
transparent: false
});
}
function makeWingShape(side, isUpper) {
const group = new THREE.Group();
const s = side;
if (isUpper) {
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.bezierCurveTo(s * 0.8, 0.5, s * 1.6, 1.0, s * 2.0, 0.4);
shape.bezierCurveTo(s * 2.2, 0.0, s * 1.9, -0.5, s * 1.0, -0.7);
shape.bezierCurveTo(s * 0.5, -0.8, s * 0.1, -0.5, 0, 0);
const geo = new THREE.ShapeGeometry(shape, 14);
const mesh = new THREE.Mesh(geo, wireframeMaterial(colors.wing1));
group.add(mesh);
const edgeGeo = new THREE.EdgesGeometry(geo);
const edgeMat = new THREE.LineBasicMaterial({ color: colors.vein });
const wireEdges = new THREE.LineSegments(edgeGeo, edgeMat);
group.add(wireEdges);
for (let i = 0; i < 5; i++) {
const pt1 = new THREE.Vector3(0, 0, 0.01);
const pt2 = new THREE.Vector3(s * (0.5 + i * 0.25), (0.3 - i * 0.1), 0.01);
const pt3 = new THREE.Vector3(s * (0.8 + i * 0.3), (0.1 - i * 0.15), 0.01);
const curve = new THREE.CatmullRomCurve3([pt1, pt2, pt3]);
const tubeGeo = new THREE.TubeGeometry(curve, 5, 0.012, 4, false);
const tubeMat = wireframeMaterial(colors.vein);
group.add(new THREE.Mesh(tubeGeo, tubeMat));
}
for (let i = 0; i < 3; i++) {
const spotGeo = new THREE.CircleGeometry(0.08 + i * 0.03, 8);
const spotMat = wireframeMaterial(colors.spot);
const spot = new THREE.Mesh(spotGeo, spotMat);
spot.position.set(s * (0.9 + i * 0.5), 0.2 - i * 0.15, 0.02);
group.add(spot);
}
} else {
const lowerShape = new THREE.Shape();
lowerShape.moveTo(0, 0);
lowerShape.bezierCurveTo(s * 0.5, -0.1, s * 1.3, 0.1, s * 1.6, -0.3);
lowerShape.bezierCurveTo(s * 1.8, -0.6, s * 1.5, -1.1, s * 0.8, -1.2);
lowerShape.bezierCurveTo(s * 0.3, -1.2, s * 0.1, -0.8, 0, 0);
const geoLower = new THREE.ShapeGeometry(lowerShape, 12);
const lowerMesh = new THREE.Mesh(geoLower, wireframeMaterial(colors.wing2));
group.add(lowerMesh);
const lowerEdges = new THREE.EdgesGeometry(geoLower);
const edgeLowerMat = new THREE.LineBasicMaterial({ color: colors.vein });
group.add(new THREE.LineSegments(lowerEdges, edgeLowerMat));
for (let i = 0; i < 3; i++) {
const spotGeo = new THREE.CircleGeometry(0.07, 7);
const spotMat = wireframeMaterial(colors.spot);
const spot = new THREE.Mesh(spotGeo, spotMat);
spot.position.set(s * (0.5 + i * 0.4), -0.4 - i * 0.2, 0.02);
group.add(spot);
}
}
return group;
}
function createButterfly() {
const group = new THREE.Group();
const bodySegments = 7;
for (let i = 0; i < bodySegments; i++) {
const radius = 0.09 - i * 0.008;
const yPos = 0.5 - i * 0.22;
const useDark = (i % 2 === 0);
const bodyMat = wireframeMaterial(useDark ? colors.dark : colors.body);
const segmentGeo = new THREE.SphereGeometry(radius, 10, 10);
const segment = new THREE.Mesh(segmentGeo, bodyMat);
segment.position.set(0, yPos, 0);
segment.scale.set(0.8, 1.0, 0.8);
group.add(segment);
}
const headGeo = new THREE.SphereGeometry(0.13, 12, 12);
const headMat = wireframeMaterial(colors.body);
const head = new THREE.Mesh(headGeo, headMat);
head.position.set(0, 0.72, 0);
group.add(head);
[-1, 1].forEach(side => {
const eyeGeo = new THREE.SphereGeometry(0.04, 7, 7);
const eyeMat = wireframeMaterial(colors.vein);
const eye = new THREE.Mesh(eyeGeo, eyeMat);
eye.position.set(side * 0.1, 0.76, 0.08);
group.add(eye);
const antPoints = [
new THREE.Vector3(side * 0.07, 0.82, 0),
new THREE.Vector3(side * 0.2, 1.1, 0.05),
new THREE.Vector3(side * 0.28, 1.35, 0.02)
];
const antCurve = new THREE.CatmullRomCurve3(antPoints);
const antTubeGeo = new THREE.TubeGeometry(antCurve, 8, 0.012, 4, false);
const antMat = wireframeMaterial(colors.vein);
group.add(new THREE.Mesh(antTubeGeo, antMat));
const knobGeo = new THREE.SphereGeometry(0.035, 7, 7);
const knobMat = wireframeMaterial(colors.spot);
const knob = new THREE.Mesh(knobGeo, knobMat);
knob.position.set(side * 0.28, 1.38, 0.02);
group.add(knob);
const legYPositions = [0.2, 0.0, -0.2];
legYPositions.forEach(legY => {
const legPoints = [
new THREE.Vector3(side * 0.08, legY, 0),
new THREE.Vector3(side * 0.25, legY - 0.15, 0.05),
new THREE.Vector3(side * 0.35, legY - 0.35, 0.0)
];
const legCurve = new THREE.CatmullRomCurve3(legPoints);
const legGeo = new THREE.TubeGeometry(legCurve, 5, 0.012, 4, false);
const legMat = wireframeMaterial(colors.dark);
group.add(new THREE.Mesh(legGeo, legMat));
});
});
const leftWingPivot = new THREE.Group();
const rightWingPivot = new THREE.Group();
const leftUpperWing = makeWingShape(-1, true);
const leftLowerWing = makeWingShape(-1, false);
const rightUpperWing = makeWingShape(1, true);
const rightLowerWing = makeWingShape(1, false);
leftWingPivot.add(leftUpperWing);
leftWingPivot.add(leftLowerWing);
rightWingPivot.add(rightUpperWing);
rightWingPivot.add(rightLowerWing);
group.add(leftWingPivot);
group.add(rightWingPivot);
leftUpper = leftWingPivot;
rightUpper = rightWingPivot;
group.position.set(0, 0, 0);
return group;
}
function init() {
updateSize();
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);
const wrap = document.getElementById('bfly-wrap');
camera.aspect = wrap.offsetWidth / wrap.offsetHeight;
camera.position.set(0, 0.2, 6);
camera.lookAt(0, 0.2, 0);
camera.updateProjectionMatrix();
butterfly = createButterfly();
scene.add(butterfly);
const ambientLight = new THREE.AmbientLight(0x404060, 0.5);
scene.add(ambientLight);
const pointLight1 = new THREE.PointLight(0x5bcaaa, 0.9);
pointLight1.position.set(3, 4, 4);
scene.add(pointLight1);
const pointLight2 = new THREE.PointLight(0xfac775, 0.7);
pointLight2.position.set(-3, 2, 5);
scene.add(pointLight2);
animate();
}
function animate() {
requestAnimationFrame(animate);
const time = Date.now() * 0.001;
if (butterfly) {
butterfly.position.y = Math.sin(time * 1.4) * 0.12;
butterfly.rotation.z = Math.sin(time * 2.0) * 0.02;
butterfly.rotation.y += 0.004;
const flapAngle = Math.sin(time * 3.5) * 0.55;
if (leftUpper) leftUpper.rotation.y = flapAngle;
if (rightUpper) rightUpper.rotation.y = -flapAngle;
}
renderer.render(scene, camera);
}
window.addEventListener('resize', () => {
updateSize();
if (camera) {
const wrap = document.getElementById('bfly-wrap');
camera.aspect = wrap.offsetWidth / wrap.offsetHeight;
camera.updateProjectionMatrix();
}
});
init();
})();
</script>
</body>
</html>