diff --git a/README.md b/README.md
index 2f8de6e..43edfe7 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,59 @@
# Particle-Sim
-a silly little particle simulator
+
+A simple 3D particle simulation to explore particles with interesting properties.
+
+## Features
+
+- **3D Particle System**: Real-time 3D particle simulation with proper depth perception
+- **Physics Engine**: Gravity, attraction forces, and inter-particle interactions
+- **Interactive Controls**: Adjust simulation parameters in real-time
+- **Visual Effects**:
+ - Depth-based particle sizing and transparency
+ - Colorful particles with HSL color system
+ - Optional motion trails
+ - Glow effects
+- **Rotation**: Automatic 3D rotation for better visualization
+- **Performance Monitoring**: Real-time FPS and particle count display
+
+## How to Use
+
+1. Open `index.html` in a modern web browser
+2. Use the control panel on the right to adjust simulation parameters:
+ - **Particle Count**: Number of particles (10-500)
+ - **Gravity**: Vertical force applied to particles (-1 to 1)
+ - **Attraction Force**: Force pulling particles to center (-2 to 2)
+ - **Particle Size**: Base size of particles (1-10)
+ - **Speed**: Simulation speed multiplier (0.1-3)
+ - **Rotation Speed**: Speed of 3D rotation (0-2)
+3. Toggle effects:
+ - **Toggle Trails**: Show/hide particle motion trails
+ - **Toggle Depth Effect**: Enable/disable depth-based sizing
+ - **Reset Simulation**: Reset all particles to initial positions
+
+## Interesting Properties
+
+The particles exhibit several interesting behaviors:
+
+- **Gravitational Pull**: Particles are attracted to the center, creating swirling patterns
+- **Inter-particle Forces**: Nearby particles interact with each other
+- **3D Motion**: Particles move freely in 3D space with realistic physics
+- **Color Variety**: Each particle has a unique color based on its initial position
+- **Boundary Wrapping**: Particles wrap around when they reach the edges
+
+## Technical Details
+
+- Pure HTML5, CSS3, and JavaScript (no external dependencies)
+- Canvas-based rendering with 3D to 2D projection
+- Verlet integration for smooth physics simulation
+- Optimized rendering with depth sorting for proper visual layering
+
+## Screenshots
+
+### Default View
+
+
+### With Trails Enabled
+
+
+### High Particle Count
+
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..9f58080
--- /dev/null
+++ b/index.html
@@ -0,0 +1,171 @@
+
+
+
+
+
+ 3D Particle Simulation
+
+
+
+
+
+
+
+
+
3D Particle Sim
+
+
+ Particle Count: 100
+
+
+
+
+ Gravity: 0.1
+
+
+
+
+ Attraction Force: 0.5
+
+
+
+
+ Particle Size: 3
+
+
+
+
+ Speed: 1.0
+
+
+
+
+ Rotation Speed: 0.5
+
+
+
+
Reset Simulation
+
Toggle Trails
+
Toggle Depth Effect
+
+
+
FPS: 0
+
Particles: 0
+
+
+
+
+
+
+
diff --git a/simulation.js b/simulation.js
new file mode 100644
index 0000000..82c4b29
--- /dev/null
+++ b/simulation.js
@@ -0,0 +1,351 @@
+// 3D Particle Simulation
+class Particle {
+ constructor(x, y, z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ this.vx = (Math.random() - 0.5) * 2;
+ this.vy = (Math.random() - 0.5) * 2;
+ this.vz = (Math.random() - 0.5) * 2;
+
+ this.ax = 0;
+ this.ay = 0;
+ this.az = 0;
+
+ // Color based on initial position
+ this.hue = Math.random() * 360;
+ this.saturation = 70 + Math.random() * 30;
+ this.lightness = 50 + Math.random() * 20;
+
+ this.mass = 1;
+ this.trail = [];
+ this.maxTrailLength = 10;
+ }
+
+ applyForce(fx, fy, fz) {
+ this.ax += fx / this.mass;
+ this.ay += fy / this.mass;
+ this.az += fz / this.mass;
+ }
+
+ update(speedMultiplier) {
+ // Update velocity with acceleration
+ this.vx += this.ax * speedMultiplier;
+ this.vy += this.ay * speedMultiplier;
+ this.vz += this.az * speedMultiplier;
+
+ // Apply damping
+ this.vx *= 0.99;
+ this.vy *= 0.99;
+ this.vz *= 0.99;
+
+ // Store trail
+ if (config.showTrails) {
+ this.trail.push({ x: this.x, y: this.y, z: this.z });
+ if (this.trail.length > this.maxTrailLength) {
+ this.trail.shift();
+ }
+ } else {
+ this.trail = [];
+ }
+
+ // Update position
+ this.x += this.vx * speedMultiplier;
+ this.y += this.vy * speedMultiplier;
+ this.z += this.vz * speedMultiplier;
+
+ // Reset acceleration
+ this.ax = 0;
+ this.ay = 0;
+ this.az = 0;
+
+ // Boundary wrapping
+ if (this.x > config.boundary) this.x = -config.boundary;
+ if (this.x < -config.boundary) this.x = config.boundary;
+ if (this.y > config.boundary) this.y = -config.boundary;
+ if (this.y < -config.boundary) this.y = config.boundary;
+ if (this.z > config.boundary) this.z = -config.boundary;
+ if (this.z < -config.boundary) this.z = config.boundary;
+ }
+}
+
+// Configuration
+const config = {
+ particleCount: 100,
+ gravity: 0.1,
+ attraction: 0.5,
+ particleSize: 3,
+ speed: 1.0,
+ rotationSpeed: 0.5,
+ showTrails: false,
+ showDepth: true,
+ fov: 500,
+ cameraZ: 1000,
+ boundary: 400
+};
+
+// Canvas setup
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+
+canvas.width = window.innerWidth - 300;
+canvas.height = window.innerHeight;
+
+const centerX = canvas.width / 2;
+const centerY = canvas.height / 2;
+
+// Particles array
+let particles = [];
+
+// Rotation angles
+let angleX = 0;
+let angleY = 0;
+
+// Initialize particles
+function initParticles() {
+ particles = [];
+ for (let i = 0; i < config.particleCount; i++) {
+ const x = (Math.random() - 0.5) * 400;
+ const y = (Math.random() - 0.5) * 400;
+ const z = (Math.random() - 0.5) * 400;
+ particles.push(new Particle(x, y, z));
+ }
+}
+
+// 3D to 2D projection
+function project(x, y, z) {
+ const scale = config.fov / (config.fov + z + config.cameraZ);
+ const x2d = x * scale + centerX;
+ const y2d = y * scale + centerY;
+ return { x: x2d, y: y2d, scale };
+}
+
+// Rotate point around X axis
+function rotateX(x, y, z, angle) {
+ const cos = Math.cos(angle);
+ const sin = Math.sin(angle);
+ return {
+ x: x,
+ y: y * cos - z * sin,
+ z: y * sin + z * cos
+ };
+}
+
+// Rotate point around Y axis
+function rotateY(x, y, z, angle) {
+ const cos = Math.cos(angle);
+ const sin = Math.sin(angle);
+ return {
+ x: x * cos + z * sin,
+ y: y,
+ z: -x * sin + z * cos
+ };
+}
+
+// Apply forces to particles
+function applyForces() {
+ particles.forEach(particle => {
+ // Gravity
+ particle.applyForce(0, config.gravity, 0);
+
+ // Attraction to center
+ const dx = 0 - particle.x;
+ const dy = 0 - particle.y;
+ const dz = 0 - particle.z;
+ const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
+
+ if (distance > 0) {
+ const force = config.attraction / (distance * distance + 1);
+ particle.applyForce(dx * force, dy * force, dz * force);
+ }
+
+ // Inter-particle forces (only for nearby particles)
+ particles.forEach(other => {
+ if (particle !== other) {
+ const dx = other.x - particle.x;
+ const dy = other.y - particle.y;
+ const dz = other.z - particle.z;
+ const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
+
+ if (distance < 50 && distance > 0) {
+ const force = 0.01 / (distance * distance + 1);
+ particle.applyForce(dx * force, dy * force, dz * force);
+ }
+ }
+ });
+ });
+}
+
+// Update rotation
+function updateRotation() {
+ angleY += config.rotationSpeed * 0.01;
+ angleX += config.rotationSpeed * 0.005;
+}
+
+// Draw particles
+function draw() {
+ // Clear canvas with trail effect
+ if (config.showTrails) {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ } else {
+ ctx.fillStyle = 'rgba(0, 0, 0, 1)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ // Sort particles by z-depth for proper rendering
+ const particlesToDraw = particles.map(particle => {
+ let pos = rotateX(particle.x, particle.y, particle.z, angleX);
+ pos = rotateY(pos.x, pos.y, pos.z, angleY);
+ const projected = project(pos.x, pos.y, pos.z);
+ return {
+ particle,
+ pos,
+ projected,
+ depth: pos.z
+ };
+ }).sort((a, b) => a.depth - b.depth);
+
+ // Draw trails
+ if (config.showTrails) {
+ particlesToDraw.forEach(({ particle, pos }) => {
+ if (particle.trail.length > 1) {
+ ctx.strokeStyle = `hsla(${particle.hue}, ${particle.saturation}%, ${particle.lightness}%, 0.3)`;
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+
+ particle.trail.forEach((point, index) => {
+ let trailPos = rotateX(point.x, point.y, point.z, angleX);
+ trailPos = rotateY(trailPos.x, trailPos.y, trailPos.z, angleY);
+ const trailProjected = project(trailPos.x, trailPos.y, trailPos.z);
+
+ if (index === 0) {
+ ctx.moveTo(trailProjected.x, trailProjected.y);
+ } else {
+ ctx.lineTo(trailProjected.x, trailProjected.y);
+ }
+ });
+
+ ctx.stroke();
+ }
+ });
+ }
+
+ // Draw particles
+ particlesToDraw.forEach(({ particle, pos, projected, depth }) => {
+ let size = config.particleSize;
+ let alpha = 1;
+
+ if (config.showDepth) {
+ // Size based on depth
+ size = config.particleSize * projected.scale * 2;
+ // Alpha based on depth
+ alpha = Math.max(0.2, Math.min(1, (depth + config.cameraZ) / (config.cameraZ * 1.5)));
+ }
+
+ // Draw particle
+ ctx.beginPath();
+ ctx.arc(projected.x, projected.y, size, 0, Math.PI * 2);
+ ctx.fillStyle = `hsla(${particle.hue}, ${particle.saturation}%, ${particle.lightness}%, ${alpha})`;
+ ctx.fill();
+
+ // Add glow effect
+ if (size > 2) {
+ ctx.beginPath();
+ ctx.arc(projected.x, projected.y, size * 1.5, 0, Math.PI * 2);
+ ctx.fillStyle = `hsla(${particle.hue}, ${particle.saturation}%, ${particle.lightness}%, ${alpha * 0.2})`;
+ ctx.fill();
+ }
+ });
+}
+
+// Animation loop
+let lastTime = Date.now();
+let fps = 0;
+let frameCount = 0;
+let fpsUpdateTime = Date.now();
+
+function animate() {
+ const currentTime = Date.now();
+ const deltaTime = currentTime - lastTime;
+ lastTime = currentTime;
+
+ // Update FPS
+ frameCount++;
+ if (currentTime - fpsUpdateTime > 1000) {
+ fps = frameCount;
+ frameCount = 0;
+ fpsUpdateTime = currentTime;
+ document.getElementById('fps').textContent = fps;
+ document.getElementById('particleStats').textContent = particles.length;
+ }
+
+ // Update physics
+ applyForces();
+ particles.forEach(particle => particle.update(config.speed));
+
+ // Update rotation
+ updateRotation();
+
+ // Draw
+ draw();
+
+ requestAnimationFrame(animate);
+}
+
+// Control handlers
+document.getElementById('particleCount').addEventListener('input', (e) => {
+ config.particleCount = parseInt(e.target.value);
+ document.getElementById('particleCountValue').textContent = config.particleCount;
+ initParticles();
+});
+
+document.getElementById('gravity').addEventListener('input', (e) => {
+ config.gravity = parseFloat(e.target.value);
+ document.getElementById('gravityValue').textContent = config.gravity.toFixed(2);
+});
+
+document.getElementById('attraction').addEventListener('input', (e) => {
+ config.attraction = parseFloat(e.target.value);
+ document.getElementById('attractionValue').textContent = config.attraction.toFixed(1);
+});
+
+document.getElementById('particleSize').addEventListener('input', (e) => {
+ config.particleSize = parseFloat(e.target.value);
+ document.getElementById('particleSizeValue').textContent = config.particleSize.toFixed(1);
+});
+
+document.getElementById('speed').addEventListener('input', (e) => {
+ config.speed = parseFloat(e.target.value);
+ document.getElementById('speedValue').textContent = config.speed.toFixed(1);
+});
+
+document.getElementById('rotationSpeed').addEventListener('input', (e) => {
+ config.rotationSpeed = parseFloat(e.target.value);
+ document.getElementById('rotationSpeedValue').textContent = config.rotationSpeed.toFixed(1);
+});
+
+document.getElementById('reset').addEventListener('click', () => {
+ initParticles();
+ angleX = 0;
+ angleY = 0;
+});
+
+document.getElementById('toggleTrails').addEventListener('click', () => {
+ config.showTrails = !config.showTrails;
+});
+
+document.getElementById('toggleDepth').addEventListener('click', () => {
+ config.showDepth = !config.showDepth;
+});
+
+// Handle window resize
+window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth - 300;
+ canvas.height = window.innerHeight;
+});
+
+// Start simulation
+initParticles();
+animate();