<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Solar System Element</title>
<style>
body { margin: 0; background-color: #000; }
/* This is the container for the custom element. You can change its size and position. */
#solar-system-container {
width: 1000px;
height: 1000px;
}
</style>
</head>
<body>
<div id="solar-system-container">
<solar-system-animation></solar-system-animation>
</div>
<script>
class SolarSystemAnimation extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// Define the styles for the component's container and canvas
const style = document.createElement('style');
style.textContent = `
:host {
display: block;
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
background-color: #000;
}
`;
this.shadowRoot.appendChild(style);
// Load the three.js library dynamically. The animation will start once it's loaded.
const threeScript = document.createElement('script');
threeScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js';
threeScript.onload = () => this.initAnimation();
this.shadowRoot.appendChild(threeScript);
}
initAnimation() {
// --- SCENE SETUP ---
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, this.clientWidth / this.clientHeight, 0.1, 5000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(this.clientWidth, this.clientHeight);
this.shadowRoot.appendChild(renderer.domElement);
// Create a group for the camera to control its orbit
const cameraGroup = new THREE.Group();
scene.add(cameraGroup);
cameraGroup.add(camera);
// Set initial camera position and tilt
camera.position.z = 500;
camera.position.y = 100;
cameraGroup.rotation.x = 15 * (Math.PI / 180);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// --- LIGHTING ---
// A strong light at the center to represent the Sun
const sunLight = new THREE.PointLight(0xffffff, 2, 0, 2);
scene.add(sunLight);
const ambientLight = new THREE.AmbientLight(0x404040); // Soft white light
scene.add(ambientLight);
// --- PLANET DATA (scaled for a better fit) ---
const planets = [
{ name: 'Mercury', size: 0.45, distance: 15, color: 0x8A8A8A, speed: 0.005, moons: [] },
{ name: 'Venus', size: 1.35, distance: 20, color: 0xC19A6B, speed: 0.004, moons: [] },
{
name: 'Earth',
size: 1.5,
distance: 25,
color: 0x228B22,
speed: 0.003,
moons: [
{ name: 'Moon', size: 0.375, distance: 4.5, color: 0xC0C0C0, speed: 0.02 }
]
},
{
name: 'Mars',
size: 0.75,
distance: 30,
color: 0x800000,
speed: 0.002,
moons: [
{ name: 'Phobos', size: 0.15, distance: 2.25, color: 0x696969, speed: 0.03 },
{ name: 'Deimos', size: 0.12, distance: 3.75, color: 0xA9A9A9, speed: 0.025 }
]
},
{
name: 'Jupiter',
size: 7.5,
distance: 75,
color: 0xBD8948,
speed: 0.001,
moons: [
{ name: 'Io', size: 0.6, distance: 10.5, color: 0xFFD700, speed: 0.02 },
{ name: 'Europa', size: 0.525, distance: 13.5, color: 0xADD8E6, speed: 0.018 },
{ name: 'Ganymede', size: 0.825, distance: 18, color: 0x778899, speed: 0.015 },
{ name: 'Callisto', size: 0.75, distance: 24, color: 0x696969, speed: 0.012 },
{ name: 'Amalthea', size: 0.2, distance: 8, color: 0xAAAAAA, speed: 0.025 }
]
},
{
name: 'Saturn',
size: 6.75,
distance: 125,
color: 0xBDAF80,
speed: 0.0008,
moons: [
{ name: 'Titan', size: 0.75, distance: 12, color: 0xD2B48C, speed: 0.01 },
{ name: 'Rhea', size: 0.45, distance: 16, color: 0xD2B48C, speed: 0.012 },
]
},
{ name: 'Uranus', size: 3.0, distance: 175, color: 0xACE5EE, speed: 0.0006, moons: [{ name: 'Titania', size: 0.4, distance: 8, color: 0xD3D3D3, speed: 0.015 }] },
{ name: 'Neptune', size: 2.85, distance: 250, color: 0x4169E1, speed: 0.0005, moons: [{ name: 'Triton', size: 0.45, distance: 9, color: 0xA9A9A9, speed: 0.012 }] }
];
// --- OBJECT CREATION ---
// Create the Sun
const sunGeometry = new THREE.SphereGeometry(15, 32, 32);
const sunMaterial = new THREE.MeshBasicMaterial({ color: 0xFFFF00 });
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
scene.add(sun);
// Create each planet, its moons, and a trail
const planetGroups = [];
const maxTrailPoints = 10000;
planets.forEach(p => {
const planetGroup = new THREE.Group();
scene.add(planetGroup);
// Planet Mesh
const planetGeometry = new THREE.SphereGeometry(p.size, 16, 16);
const planetMaterial = new THREE.MeshPhongMaterial({ color: p.color });
const planetMesh = new THREE.Mesh(planetGeometry, planetMaterial);
planetMesh.position.x = p.distance;
planetGroup.add(planetMesh);
// Saturn's Rings
if (p.name === 'Saturn') {
const ringGeometry = new THREE.RingGeometry(p.size * 1.2, p.size * 1.8, 64);
const ringMaterial = new THREE.MeshBasicMaterial({ color: 0x8B7355, side: THREE.DoubleSide });
const rings = new THREE.Mesh(ringGeometry, ringMaterial);
rings.rotation.x = -Math.PI / 2;
planetMesh.add(rings);
}
// Moons
const moonGroup = new THREE.Group();
planetMesh.add(moonGroup);
p.moons.forEach((m, moonIndex) => {
const moonGeometry = new THREE.SphereGeometry(m.size, 16, 16);
const moonMaterial = new THREE.MeshPhongMaterial({ color: m.color });
const moonMesh = new THREE.Mesh(moonGeometry, moonMaterial);
// Stagger the moon positions
const angle = (2 * Math.PI / p.moons.length) * moonIndex;
moonMesh.position.x = m.distance * Math.cos(angle);
moonMesh.position.z = m.distance * Math.sin(angle);
moonGroup.add(moonMesh);
moonMesh.userData.speed = m.speed;
});
planetMesh.userData.moonGroup = moonGroup;
// Particle Trail
const trailGeometry = new THREE.BufferGeometry();
const positions = new Float32Array(maxTrailPoints * 3);
trailGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const trailMaterial = new THREE.PointsMaterial({ color: p.color, size: 0.1 });
const trail = new THREE.Points(trailGeometry, trailMaterial);
scene.add(trail);
trail.geometry.setDrawRange(0, 0);
planetGroups.push({
group: planetGroup,
speed: p.speed,
mesh: planetMesh,
trail: trail,
trailGeometry: trailGeometry,
trailIndex: 0
});
});
// --- ANIMATION LOOP ---
const cameraOrbitSpeed = 0.001;
function animate() {
requestAnimationFrame(animate);
// Orbit the camera
cameraGroup.rotation.y += cameraOrbitSpeed;
// Rotate planets and their orbits
planetGroups.forEach(p => {
p.group.rotation.y += p.speed;
p.mesh.rotation.y += p.speed * 5; // Axial rotation
// Moon orbits
if (p.mesh.userData.moonGroup) {
p.mesh.userData.moonGroup.children.forEach(moon => {
moon.rotation.y += moon.userData.speed;
});
}
// Update particle trail
const positions = p.trailGeometry.attributes.position.array;
const index = p.trailIndex;
const planetPosition = new THREE.Vector3();
p.mesh.getWorldPosition(planetPosition);
positions[index * 3] = planetPosition.x;
positions[index * 3 + 1] = planetPosition.y;
positions[index * 3 + 2] = planetPosition.z;
p.trailIndex = (p.trailIndex + 1) % maxTrailPoints;
p.trailGeometry.attributes.position.needsUpdate = true;
p.trailGeometry.setDrawRange(0, Math.min(p.trailIndex, maxTrailPoints));
});
renderer.render(scene, camera);
}
// --- RESIZE HANDLING ---
const resizeObserver = new ResizeObserver(entries => {
const entry = entries[0];
const { width, height } = entry.contentRect;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
});
resizeObserver.observe(this);
animate();
}
}
customElements.define('solar-system-animation', SolarSystemAnimation);
</script>
</body>
</html>