Built a 3D Morphing Engine with 8,000 Particles! ✨ (Three.js)


Youtube link Click me

In this blog i have share the source code of my youtube video

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Morphing Particle Animation</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            overflow: hidden;
            background: #000;
            font-family: Arial, sans-serif;
        }

        #canvas-container {
            width: 100vw;
            height: 100vh;
        }
    </style>
</head>
<body>
    <div id="canvas-container"></div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // Scene setup
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(
            75,
            window.innerWidth / window.innerHeight,
            0.1,
            1000
        );
        camera.position.z = 4;

        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0x000000, 1);
        document.getElementById('canvas-container').appendChild(renderer.domElement);

        // Particle system
        const particleCount = 8000;
        const positions = new Float32Array(particleCount * 3);
        const colors = new Float32Array(particleCount * 3);
        const sizes = new Float32Array(particleCount);
        const targetPositions = new Float32Array(particleCount * 3);
        const startPositions = new Float32Array(particleCount * 3);

        // Shape generators
        const shapes = {
            sphere: () => {
                const pos = new Float32Array(particleCount * 3);
                for (let i = 0; i < particleCount; i++) {
                    const i3 = i * 3;
                    const phi = Math.acos(1 - 2 * (i + 0.5) / particleCount);
                    const theta = Math.PI * (1 + Math.sqrt(5)) * i;
                    const radius = 1 + Math.random() * 0.2;
                   
                    pos[i3] = radius * Math.sin(phi) * Math.cos(theta);
                    pos[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
                    pos[i3 + 2] = radius * Math.cos(phi);
                }
                return pos;
            },
           
            humanoid: () => {
                const pos = new Float32Array(particleCount * 3);
                for (let i = 0; i < particleCount; i++) {
                    const i3 = i * 3;
                    const t = i / particleCount;
                   
                    if (t < 0.25) { // Head
                        const phi = Math.random() * Math.PI * 2;
                        const theta = Math.random() * Math.PI;
                        const r = 0.3;
                        pos[i3] = r * Math.sin(theta) * Math.cos(phi);
                        pos[i3 + 1] = 1.2 + r * Math.cos(theta);
                        pos[i3 + 2] = r * Math.sin(theta) * Math.sin(phi);
                    } else if (t < 0.5) { // Body
                        const angle = Math.random() * Math.PI * 2;
                        const radius = Math.random() * 0.3;
                        const height = Math.random() * 1.0;
                        pos[i3] = radius * Math.cos(angle);
                        pos[i3 + 1] = 0.2 + height * 0.8;
                        pos[i3 + 2] = radius * Math.sin(angle);
                    } else if (t < 0.7) { // Arms
                        const side = Math.random() < 0.5 ? -1 : 1;
                        const armLength = Math.random();
                        pos[i3] = side * (0.3 + armLength * 0.6);
                        pos[i3 + 1] = 0.8 - armLength * 0.4;
                        pos[i3 + 2] = (Math.random() - 0.5) * 0.2;
                    } else { // Legs
                        const side = Math.random() < 0.5 ? -1 : 1;
                        const legLength = Math.random();
                        pos[i3] = side * 0.15;
                        pos[i3 + 1] = 0.2 - legLength * 0.8;
                        pos[i3 + 2] = (Math.random() - 0.5) * 0.2;
                    }
                }
                return pos;
            },
           
            torus: () => {
                const pos = new Float32Array(particleCount * 3);
                for (let i = 0; i < particleCount; i++) {
                    const i3 = i * 3;
                    const u = (i / particleCount) * Math.PI * 2;
                    const v = ((i * 17) % particleCount / particleCount) * Math.PI * 2;
                    const R = 1;
                    const r = 0.4;
                   
                    pos[i3] = (R + r * Math.cos(v)) * Math.cos(u);
                    pos[i3 + 1] = (R + r * Math.cos(v)) * Math.sin(u);
                    pos[i3 + 2] = r * Math.sin(v);
                }
                return pos;
            },
           
            helix: () => {
                const pos = new Float32Array(particleCount * 3);
                for (let i = 0; i < particleCount; i++) {
                    const i3 = i * 3;
                    const t = (i / particleCount) * Math.PI * 8;
                    const radius = 0.5 + Math.random() * 0.2;
                   
                    pos[i3] = radius * Math.cos(t);
                    pos[i3 + 1] = (i / particleCount - 0.5) * 4;
                    pos[i3 + 2] = radius * Math.sin(t);
                }
                return pos;
            },
           
            cube: () => {
                const pos = new Float32Array(particleCount * 3);
                for (let i = 0; i < particleCount; i++) {
                    const i3 = i * 3;
                    pos[i3] = (Math.random() - 0.5) * 2;
                    pos[i3 + 1] = (Math.random() - 0.5) * 2;
                    pos[i3 + 2] = (Math.random() - 0.5) * 2;
                }
                return pos;
            }
        };

        const shapeNames = Object.keys(shapes);
       
        // Initialize particles
        const initialShape = shapes.sphere();
        for (let i = 0; i < particleCount; i++) {
            const i3 = i * 3;
           
            positions[i3] = initialShape[i3];
            positions[i3 + 1] = initialShape[i3 + 1];
            positions[i3 + 2] = initialShape[i3 + 2];
           
            startPositions[i3] = positions[i3];
            startPositions[i3 + 1] = positions[i3 + 1];
            startPositions[i3 + 2] = positions[i3 + 2];

            // Blue-ish colors with variation
            colors[i3] = 0.2 + Math.random() * 0.3;
            colors[i3 + 1] = 0.4 + Math.random() * 0.4;
            colors[i3 + 2] = 0.8 + Math.random() * 0.2;

            sizes[i] = Math.random() * 2 + 1;
        }

        const geometry = new THREE.BufferGeometry();
        geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
        geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));

        const material = new THREE.PointsMaterial({
            size: 0.015,
            vertexColors: true,
            transparent: true,
            opacity: 0.9,
            sizeAttenuation: true,
            blending: THREE.AdditiveBlending
        });

        const particles = new THREE.Points(geometry, material);
        scene.add(particles);

        // Animation state
        let time = 0;
        let shapeIndex = 0;
        let lastShapeChange = 0;
        const transitionDuration = 3; // seconds
        const holdDuration = 2; // seconds

        // Set initial target
        const firstTarget = shapes[shapeNames[1]]();
        for (let i = 0; i < particleCount * 3; i++) {
            targetPositions[i] = firstTarget[i];
        }

        // Animation loop
        function animate() {
            requestAnimationFrame(animate);
            time += 0.016; // ~60fps

            const elapsed = time - lastShapeChange;
            const totalCycle = transitionDuration + holdDuration;

            // Check if we need to switch to next shape
            if (elapsed > totalCycle) {
                lastShapeChange = time;
                shapeIndex = (shapeIndex + 1) % shapeNames.length;
               
                // Update start positions
                for (let i = 0; i < particleCount * 3; i++) {
                    startPositions[i] = positions[i];
                }
               
                // Set new target
                const nextShape = shapes[shapeNames[shapeIndex]]();
                for (let i = 0; i < particleCount * 3; i++) {
                    targetPositions[i] = nextShape[i];
                }
            }

            // Calculate morph progress (0 to 1)
            if (elapsed < transitionDuration) {
                const progress = elapsed / transitionDuration;
                // Smooth easing
                const eased = progress < 0.5
            ? 2 * progress * progress
        : 1-Math.pow(-2 * progress + 2, 2) /2
   
    //Interpolate positions
                for (let i=0; i < particleCount * 3; i++) {
                    positions[i] = startPositions[i] + (targetPositions[i] - startPositions[i]) * eased;
                }
            geometry.attributes.position.needsUpdate = true;
        }

        //Gentle rotation
        particles.rotation.y += 0.002;
        particles.rotation.x = Math.sin(time * 0.1) * 0.1;
        renderer.render(scene, camera);
    }
        animate();

        //Handle resizing
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
        </script>
</body>
</html>


Comments

Popular posts from this blog

Birthday card in html css js

Create Animated Tooltip Popups in HTML/CSS | No JavaScript Needed!

Create an Instagram Profile Page Using HTML and CSS | UI Clone Tutorial

JavaScript Voronoi Circles effect |HTML JS| #coding

Digital Clock with animation |HTML|CSS|JS

Coding a 'Smart' Particle System (Text to Dots Tutorial)

MOST EXPENSIVE AND EXTREME GAMING SETUPS EVER