<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Christmas Tree</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.140.1/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.140.1/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.2/dist/gsap.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #1a1a2e;
}
#info {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-family: Arial, sans-serif;
z-index: 100;
}
</style>
</head>
<body>
<div id="info">
<p>Click and drag to rotate the scene</p>
<p>Scroll to zoom in/out</p>
</div>
<script>
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x1a1a2e);
document.body.appendChild(renderer.domElement);
// Controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
camera.position.set(0, 5, 15);
// Snow ground
const groundGeometry = new THREE.PlaneGeometry(100, 100);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
roughness: 0.8,
metalness: 0.2
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -0.5;
scene.add(ground);
// Christmas tree (Pixar style)
function createChristmasTree() {
const treeGroup = new THREE.Group();
// Tree trunk
const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.7, 2, 16);
const trunkMaterial = new THREE.MeshStandardMaterial({
color: 0x8B4513,
roughness: 0.9,
metalness: 0.1
});
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
trunk.position.y = 1;
treeGroup.add(trunk);
// Tree leaves (conical sections with rounded edges for Pixar style)
const leafColors = [0x006400, 0x228B22, 0x32CD32];
for (let i = 0; i < 5; i++) {
const radius = 2.5 - i * 0.4;
const height = 2 - i * 0.3;
const segments = 32;
// Create a cone with rounded top
const leafGeometry = new THREE.ConeGeometry(radius, height, segments);
const leafMaterial = new THREE.MeshStandardMaterial({
color: leafColors[i % leafColors.length],
roughness: 0.8,
metalness: 0.2,
shininess: 30
});
const leaf = new THREE.Mesh(leafGeometry, leafMaterial);
leaf.position.y = 3 + i * 0.8;
// Add slight curve for more organic look
leaf.scale.set(1, 1.1, 1);
treeGroup.add(leaf);
}
// Tree topper (star)
const starGeometry = new THREE.IcosahedronGeometry(0.5, 0);
const starMaterial = new THREE.MeshStandardMaterial({
color: 0xFFD700,
emissive: 0xFFD700,
emissiveIntensity: 0.3,
roughness: 0.2,
metalness: 0.8
});
const star = new THREE.Mesh(starGeometry, starMaterial);
star.position.y = 7.5;
star.scale.set(1.2, 1.5, 1.2);
treeGroup.add(star);
// Decorations
const decorationColors = [0xFF0000, 0xFFA500, 0xFFC0CB, 0x800080, 0x00BFFF];
for (let i = 0; i < 30; i++) {
const decorationGeometry = new THREE.SphereGeometry(0.2, 16, 16);
const decorationMaterial = new THREE.MeshStandardMaterial({
color: decorationColors[i % decorationColors.length],
emissive: decorationColors[i % decorationColors.length],
emissiveIntensity: 0.2,
roughness: 0.1,
metalness: 0.9
});
const decoration = new THREE.Mesh(decorationGeometry, decorationMaterial);
// Random position on tree
const angle = Math.random() * Math.PI * 2;
const radius = Math.random() * 2 + 0.5;
const height = Math.random() * 5 + 2;
decoration.position.x = Math.cos(angle) * radius;
decoration.position.z = Math.sin(angle) * radius;
decoration.position.y = height;
// Check if decoration is inside the tree
const maxRadiusAtHeight = 2.5 - (height - 2) * 0.4;
if (Math.sqrt(decoration.position.x ** 2 + decoration.position.z ** 2) < maxRadiusAtHeight) {
treeGroup.add(decoration);
}
}
return treeGroup;
}
const christmasTree = createChristmasTree();
scene.add(christmasTree);
// Santa Claus
function createSanta() {
const santaGroup = new THREE.Group();
// Body
const bodyGeometry = new THREE.SphereGeometry(0.8, 32, 32);
const bodyMaterial = new THREE.MeshStandardMaterial({
color: 0xFF0000,
roughness: 0.8,
metalness: 0.2
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = 1.2;
santaGroup.add(body);
// Head
const headGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const headMaterial = new THREE.MeshStandardMaterial({
color: 0xFFDAB9,
roughness: 0.9,
metalness: 0.1
});
const head = new THREE.Mesh(headGeometry, headMaterial);
head.position.y = 2.5;
santaGroup.add(head);
// Hat
const hatGeometry = new THREE.ConeGeometry(0.5, 0.8, 32);
const hatMaterial = new THREE.MeshStandardMaterial({
color: 0xFF0000,
roughness: 0.8,
metalness: 0.2
});
const hat = new THREE.Mesh(hatGeometry, hatMaterial);
hat.position.y = 3.2;
santaGroup.add(hat);
// Beard
const beardGeometry = new THREE.ConeGeometry(0.4, 0.6, 32);
const beardMaterial = new THREE.MeshStandardMaterial({
color: 0xFFFFFF,
roughness: 0.9,
metalness: 0.1
});
const beard = new THREE.Mesh(beardGeometry, beardMaterial);
beard.position.y = 2.2;
beard.rotation.x = Math.PI;
santaGroup.add(beard);
// Position Santa to the side
santaGroup.position.set(-5, 0, 3);
santaGroup.scale.set(0.8, 0.8, 0.8);
return santaGroup;
}
const santa = createSanta();
scene.add(santa);
// Snowflakes
function createSnowflakes(count) {
const snowflakes = [];
const snowflakeGeometry = new THREE.SphereGeometry(0.05, 8, 8);
const snowflakeMaterial = new THREE.MeshStandardMaterial({
color: 0xFFFFFF,
transparent: true,
opacity: 0.8,
roughness: 0.1,
metalness: 0.9
});
for (let i = 0; i < count; i++) {
const snowflake = new THREE.Mesh(snowflakeGeometry, snowflakeMaterial);
// Random position
snowflake.position.x = (Math.random() - 0.5) * 50;
snowflake.position.y = Math.random() * 20 + 5;
snowflake.position.z = (Math.random() - 0.5) * 50;
// Random size
const size = Math.random() * 0.08 + 0.02;
snowflake.scale.set(size, size, size);
// Add to scene and array
scene.add(snowflake);
snowflakes.push({
mesh: snowflake,
speed: Math.random() * 0.03 + 0.01,
rotationSpeed: Math.random() * 0.05 - 0.025
});
}
return snowflakes;
}
const snowflakes = createSnowflakes(200);
// Lights
function createLights() {
// Main directional light (sun)
const dirLight = new THREE.DirectionalLight(0xFFFFFF, 1);
dirLight.position.set(5, 10, 7);
dirLight.castShadow = true;
scene.add(dirLight);
// Ambient light
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);
// Colored lights for Christmas atmosphere
const coloredLight1 = new THREE.PointLight(0xFF0000, 0.5, 10);
coloredLight1.position.set(3, 5, 3);
scene.add(coloredLight1);
const coloredLight2 = new THREE.PointLight(0x00FF00, 0.5, 10);
coloredLight2.position.set(-3, 4, 3);
scene.add(coloredLight2);
const coloredLight3 = new THREE.PointLight(0xFFFF00, 0.5, 10);
coloredLight3.position.set(0, 6, -3);
scene.add(coloredLight3);
}
createLights();
// Animation
function animateSnowflakes() {
snowflakes.forEach(snowflake => {
// Move down
snowflake.mesh.position.y -= snowflake.speed;
// Rotate
snowflake.mesh.rotation.x += snowflake.rotationSpeed;
snowflake.mesh.rotation.y += snowflake.rotationSpeed;
// Reset if fallen below ground
if (snowflake.mesh.position.y < -1) {
snowflake.mesh.position.y = 20;
snowflake.mesh.position.x = (Math.random() - 0.5) * 50;
snowflake.mesh.position.z = (Math.random() - 0.5) * 50;
}
});
}
// Animate decorations
function animateDecorations() {
christmasTree.children.forEach(child => {
if (child.material && child.material.emissive) {
// Pulsing effect for decorations
const time = Date.now() * 0.001;
const pulse = Math.sin(time * 3) * 0.2 + 0.8;
child.material.emissiveIntensity = pulse * 0.3;
}
});
}
// Main animation loop
function animate() {
requestAnimationFrame(animate);
controls.update();
animateSnowflakes();
animateDecorations();
// Rotate Santa slightly
santa.rotation.y = Math.sin(Date.now() * 0.001) * 0.3;
renderer.render(scene, camera);
}
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Start animation
animate();
// Add some initial animation with GSAP
gsap.from(christmasTree.scale, {
x: 0.1,
y: 0.1,
z: 0.1,
duration: 1.5,
ease: "back.out(1.7)"
});
gsap.from(santa.position, {
x: -10,
duration: 2,
delay: 0.5,
ease: "power2.out"
});
gsap.from(snowflakes, {
opacity: 0,
duration: 2,
stagger: 0.01
});
</script>
</body>
</html>