Close Menu

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    How to Make Subway Super Hopper Game in HTML CSS & JavaScript

    15 February 2026

    How to make Magic Social Share Menu using HTML CSS and JS

    5 February 2026

    How to Make Memory Unmasked Game in HTML CSS & JavaScript

    4 February 2026
    Facebook X (Twitter) Instagram YouTube Telegram Threads
    Coding StellaCoding Stella
    • Home
    • Blog
    • HTML & CSS
      • Login Form
    • JavaScript
    • Hire us!
    Coding StellaCoding Stella
    Home - JavaScript - How to create Particle to 3D Text Animation using HTML CSS and JS
    JavaScript

    How to create Particle to 3D Text Animation using HTML CSS and JS

    Coding StellaBy Coding Stella26 January 2026Updated:27 January 2026No Comments10 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Email WhatsApp Copy Link

    Let’s create a Particle to 3D Text Animation using HTML, CSS, and JavaScript. This interactive project turns thousands of particles into a rotating 3D sphere that morphs into any text you type, making the effect visually stunning and fun to use.

    We’ll use:

    • HTML to structure the page, input field, and animation container.
    • CSS to style the UI, add glassmorphism effects, and smooth transitions.
    • JavaScript with Three.js and GSAP to render particles, handle text input, and animate smooth morphing between shapes.

    Whether you’re a beginner or an experienced developer, this project is a great way to practice 3D graphics, animations, and interactivity in web design. Let’s bring particles to life and turn text into 3D magic. ✨

    HTML :

    This HTML sets up a page for a particle-to-3D text animation by loading styles, Google fonts, and external libraries like Three.js for 3D rendering and GSAP for animations. It creates a container for the animation, an input box to type text, and a button to trigger the effect, while script.js handles the actual animation logic.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Particle to 3D Text Animation | @coding.stella</title>
        <link rel="stylesheet" href="./style.css">
    </head>
    
    <body>
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.7.1/gsap.min.js"></script>
        <div id="container"></div>
    
        <div class="input-container">
            <div class="input-wrapper">
                <input type="text" id="morphText" placeholder="Type something..." maxlength="20">
                <button id="typeBtn">
                    <span class="button-content">
                        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                            <path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2"
                                stroke-linecap="round" stroke-linejoin="round" />
                        </svg>
                        <span>Create</span>
                    </span>
                </button>
            </div>
        </div>
        <script src="./script.js"></script>
    
    </body>
    </html>

    CSS :

    This CSS styles a dark full-screen 3D animation page with a fixed canvas container, floating headers, color scheme buttons, and smooth animated controls. It adds glassmorphism effects, gradients, hover transitions, and responsive input UI for typing text, making everything look modern, interactive, and mobile-friendly.

    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    
    body {
        background: #000;
        overflow: hidden;
        font-family: 'Inter', sans-serif;
        color: white;
    }
    
    #container {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    }
    
    .header {
        position: fixed;
        top: 2rem;
        left: 2rem;
        text-align: left;
        z-index: 1;
        mix-blend-mode: difference;
    }
    
    .header h1 {
        font-size: 2.5rem;
        font-weight: 900;
        line-height: 1;
        text-transform: uppercase;
        background: linear-gradient(45deg, #ff6e7f, #bfe9ff);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        opacity: 0.9;
        letter-spacing: -1px;
        filter: drop-shadow(0 0 15px rgba(255, 255, 255, 0.3));
    }
    
    .color-controls {
        position: fixed;
        top: 2rem;
        right: 2rem;
        z-index: 10;
        background: rgba(0, 0, 0, 0.3);
        padding: 1rem;
        border-radius: 15px;
        backdrop-filter: blur(10px);
        box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    }
    
    .color-scheme {
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
    }
    
    .color-scheme button {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        background: rgba(255, 255, 255, 0.1);
        border: 1px solid rgba(255, 255, 255, 0.2);
        padding: 0.5rem 1rem;
        color: white;
        border-radius: 8px;
        cursor: pointer;
        transition: all 0.3s ease;
        font-size: 0.9rem;
        min-width: 120px;
    }
    
    .color-scheme button:hover {
        background: rgba(255, 255, 255, 0.2);
    }
    
    .color-scheme button.active {
        background: rgba(255, 255, 255, 0.3);
        border-color: rgba(255, 255, 255, 0.5);
    }
    
    .color-preview {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        border: 2px solid rgba(255, 255, 255, 0.3);
    }
    
    .color-preview.cosmic {
        background: linear-gradient(45deg, #ff6e7f, #bfe9ff);
    }
    
    .color-preview.neon {
        background: linear-gradient(45deg, #00ff87, #60efff);
    }
    
    .color-preview.sunset {
        background: linear-gradient(45deg, #ff8c37, #ff427a);
    }
    
    .color-preview.ocean {
        background: linear-gradient(45deg, #0082c8, #00b4db);
    }
    
    .controls {
        position: fixed;
        bottom: 2rem;
        left: 50%;
        transform: translateX(-50%);
        display: flex;
        gap: 1rem;
        z-index: 10;
        background: rgba(0, 0, 0, 0.3);
        padding: 1rem;
        border-radius: 50px;
        backdrop-filter: blur(10px);
        box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
    }
    
    .controls button {
        background: rgba(255, 255, 255, 0.1);
        border: 1px solid rgba(255, 255, 255, 0.2);
        padding: 0.8rem 1.5rem;
        color: white;
        border-radius: 25px;
        cursor: pointer;
        transition: all 0.3s ease;
        text-transform: uppercase;
        letter-spacing: 1px;
        font-size: 0.9rem;
        font-weight: 500;
        min-width: 120px;
    }
    
    .controls button:hover {
        background: rgba(255, 255, 255, 0.2);
        transform: translateY(-2px);
        box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    }
    
    .controls button.active {
        background: linear-gradient(45deg, #ff6e7f, #bfe9ff);
        border: none;
        color: #000;
    }
    
    @keyframes float {
        0% {
            transform: translate(-50%, -50%);
        }
    
        50% {
            transform: translate(-50%, -52%);
        }
    
        100% {
            transform: translate(-50%, -50%);
        }
    }
    
    .content {
        animation: float 4s ease-in-out infinite;
    }
    
    .input-container {
        position: fixed;
        bottom: 2rem;
        left: 50%;
        transform: translateX(-50%);
        z-index: 10;
        width: 90%;
        max-width: 600px;
        padding: 0 1rem;
    }
    
    .input-wrapper {
        background: rgba(255, 255, 255, 0.1);
        backdrop-filter: blur(10px);
        border: 1px solid rgba(255, 255, 255, 0.2);
        border-radius: 16px;
        padding: 0.5rem;
        display: flex;
        gap: 0.5rem;
        box-shadow: 0 4px 24px -1px rgba(0, 0, 0, 0.2);
        transition: all 0.3s ease;
    }
    
    .input-wrapper:hover {
        background: rgba(255, 255, 255, 0.15);
        border-color: rgba(255, 255, 255, 0.3);
        box-shadow: 0 4px 30px -1px rgba(0, 0, 0, 0.3);
    }
    
    input {
        flex: 1;
        background: transparent;
        border: none;
        padding: 1rem 1.25rem;
        color: white;
        font-size: 1rem;
        font-weight: 500;
    }
    
    input:focus {
        outline: none;
    }
    
    input::placeholder {
        color: rgba(255, 255, 255, 0.5);
    }
    
    button {
        background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
        border: none;
        padding: 0.75rem 1.5rem;
        color: white;
        border-radius: 12px;
        font-weight: 600;
        cursor: pointer;
        transition: all 0.3s ease;
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }
    
    button:hover {
        transform: translateY(-1px);
        box-shadow: 0 4px 20px -2px rgba(79, 70, 229, 0.5);
    }
    
    button:active {
        transform: translateY(1px);
    }
    
    .button-content {
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }
    
    .button-content svg {
        width: 20px;
        height: 20px;
        transition: transform 0.3s ease;
    }
    
    button:hover .button-content svg {
        transform: translateX(3px);
    }
    
    @media (max-width: 640px) {
        .input-container {
            bottom: 1.5rem;
            padding: 0 0.75rem;
        }
    
        button {
            padding: 0.75rem 1.25rem;
        }
    
        .button-content span {
            display: none;
        }
    }

    JavaScript:

    This JavaScript uses Three.js to create thousands of particles arranged in a rotating 3D sphere, then morphs them into typed text using a hidden canvas and GSAP animations. When you enter text, particles smoothly move to form the letters, then return back to the sphere, with colors and motion updated in real time.

    let scene, camera, renderer, particles;
    const count = 12000;
    let currentState = 'sphere';
    
    function init() {
        scene = new THREE.Scene();
        camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0x000000);
        document.getElementById('container').appendChild(renderer.domElement);
    
        camera.position.z = 25;
    
        createParticles();
        setupEventListeners();
        animate();
    }
    
    function createParticles() {
        const geometry = new THREE.BufferGeometry();
        const positions = new Float32Array(count * 3);
        const colors = new Float32Array(count * 3);
    
        function sphericalDistribution(i) {
            const phi = Math.acos(-1 + (2 * i) / count);
            const theta = Math.sqrt(count * Math.PI) * phi;
    
            return {
                x: 8 * Math.cos(theta) * Math.sin(phi),
                y: 8 * Math.sin(theta) * Math.sin(phi),
                z: 8 * Math.cos(phi)
            };
        }
    
        for (let i = 0; i < count; i++) {
            const point = sphericalDistribution(i);
    
            positions[i * 3] = point.x + (Math.random() - 0.5) * 0.5;
            positions[i * 3 + 1] = point.y + (Math.random() - 0.5) * 0.5;
            positions[i * 3 + 2] = point.z + (Math.random() - 0.5) * 0.5;
    
            const color = new THREE.Color();
            const depth = Math.sqrt(point.x * point.x + point.y * point.y + point.z * point.z) / 8;
            color.setHSL(0.5 + depth * 0.2, 0.7, 0.4 + depth * 0.3);
    
            colors[i * 3] = color.r;
            colors[i * 3 + 1] = color.g;
            colors[i * 3 + 2] = color.b;
        }
    
        geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
    
        const material = new THREE.PointsMaterial({
            size: 0.08,
            vertexColors: true,
            blending: THREE.AdditiveBlending,
            transparent: true,
            opacity: 0.8,
            sizeAttenuation: true
        });
    
        if (particles) scene.remove(particles);
        particles = new THREE.Points(geometry, material);
        particles.rotation.x = 0;
        particles.rotation.y = 0;
        particles.rotation.z = 0;
        scene.add(particles);
    }
    
    function setupEventListeners() {
        const typeBtn = document.getElementById('typeBtn');
        const input = document.getElementById('morphText');
    
        typeBtn.addEventListener('click', () => {
            const text = input.value.trim();
            if (text) {
                morphToText(text);
            }
        });
    
        input.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                const text = input.value.trim();
                if (text) {
                    morphToText(text);
                }
            }
        });
    }
    
    function createTextPoints(text) {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const fontSize = 100;
        const padding = 20;
    
        ctx.font = `bold ${fontSize}px Arial`;
        const textMetrics = ctx.measureText(text);
        const textWidth = textMetrics.width;
        const textHeight = fontSize;
    
        canvas.width = textWidth + padding * 2;
        canvas.height = textHeight + padding * 2;
    
        ctx.fillStyle = 'white';
        ctx.font = `bold ${fontSize}px Arial`;
        ctx.textBaseline = 'middle';
        ctx.textAlign = 'center';
        ctx.fillText(text, canvas.width / 2, canvas.height / 2);
    
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const pixels = imageData.data;
        const points = [];
        const threshold = 128;
    
        for (let i = 0; i < pixels.length; i += 4) {
            if (pixels[i] > threshold) {
                const x = (i / 4) % canvas.width;
                const y = Math.floor((i / 4) / canvas.width);
    
                if (Math.random() < 0.3) {
                    points.push({
                        x: (x - canvas.width / 2) / (fontSize / 10),
                        y: -(y - canvas.height / 2) / (fontSize / 10)
                    });
                }
            }
        }
    
        return points;
    }
    
    function morphToText(text) {
        currentState = 'text';
        const textPoints = createTextPoints(text);
        const positions = particles.geometry.attributes.position.array;
        const targetPositions = new Float32Array(count * 3);
    
        gsap.to(particles.rotation, {
            x: 0,
            y: 0,
            z: 0,
            duration: 0.5
        });
    
        for (let i = 0; i < count; i++) {
            if (i < textPoints.length) {
                targetPositions[i * 3] = textPoints[i].x;
                targetPositions[i * 3 + 1] = textPoints[i].y;
                targetPositions[i * 3 + 2] = 0;
            } else {
                const angle = Math.random() * Math.PI * 2;
                const radius = Math.random() * 20 + 10;
                targetPositions[i * 3] = Math.cos(angle) * radius;
                targetPositions[i * 3 + 1] = Math.sin(angle) * radius;
                targetPositions[i * 3 + 2] = (Math.random() - 0.5) * 10;
            }
        }
    
        for (let i = 0; i < positions.length; i += 3) {
            gsap.to(particles.geometry.attributes.position.array, {
                [i]: targetPositions[i],
                [i + 1]: targetPositions[i + 1],
                [i + 2]: targetPositions[i + 2],
                duration: 2,
                ease: "power2.inOut",
                onUpdate: () => {
                    particles.geometry.attributes.position.needsUpdate = true;
                }
            });
        }
    
        setTimeout(() => {
            morphToCircle();
        }, 4000);
    }
    
    function morphToCircle() {
        currentState = 'sphere';
        const positions = particles.geometry.attributes.position.array;
        const targetPositions = new Float32Array(count * 3);
        const colors = particles.geometry.attributes.color.array;
    
        function sphericalDistribution(i) {
            const phi = Math.acos(-1 + (2 * i) / count);
            const theta = Math.sqrt(count * Math.PI) * phi;
    
            return {
                x: 8 * Math.cos(theta) * Math.sin(phi),
                y: 8 * Math.sin(theta) * Math.sin(phi),
                z: 8 * Math.cos(phi)
            };
        }
    
        for (let i = 0; i < count; i++) {
            const point = sphericalDistribution(i);
    
            targetPositions[i * 3] = point.x + (Math.random() - 0.5) * 0.5;
            targetPositions[i * 3 + 1] = point.y + (Math.random() - 0.5) * 0.5;
            targetPositions[i * 3 + 2] = point.z + (Math.random() - 0.5) * 0.5;
    
            const depth = Math.sqrt(point.x * point.x + point.y * point.y + point.z * point.z) / 8;
            const color = new THREE.Color();
            color.setHSL(0.5 + depth * 0.2, 0.7, 0.4 + depth * 0.3);
    
            colors[i * 3] = color.r;
            colors[i * 3 + 1] = color.g;
            colors[i * 3 + 2] = color.b;
        }
    
        for (let i = 0; i < positions.length; i += 3) {
            gsap.to(particles.geometry.attributes.position.array, {
                [i]: targetPositions[i],
                [i + 1]: targetPositions[i + 1],
                [i + 2]: targetPositions[i + 2],
                duration: 2,
                ease: "power2.inOut",
                onUpdate: () => {
                    particles.geometry.attributes.position.needsUpdate = true;
                }
            });
        }
    
        for (let i = 0; i < colors.length; i += 3) {
            gsap.to(particles.geometry.attributes.color.array, {
                [i]: colors[i],
                [i + 1]: colors[i + 1],
                [i + 2]: colors[i + 2],
                duration: 2,
                ease: "power2.inOut",
                onUpdate: () => {
                    particles.geometry.attributes.color.needsUpdate = true;
                }
            });
        }
    }
    
    function animate() {
        requestAnimationFrame(animate);
    
        if (currentState === 'sphere') {
            particles.rotation.y += 0.002;
        }
    
        renderer.render(scene, camera);
    }
    
    window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    });
    
    init();

    In conclusion, this Particle to 3D Text Animation project shows how powerful and fun web animations can be when you combine HTML, CSS, and JavaScript. By using Three.js for 3D rendering and GSAP for smooth transitions, you can create stunning visual effects that react to user input in real time.

    If your project has problems, don’t worry. Just click to download the source code and face your coding challenges with excitement. Have fun coding!

    Animation login
    Share. Copy Link Twitter Facebook LinkedIn Email WhatsApp
    Previous ArticleHow to make Crazy Pencil Loader using HTML & CSS
    Next Article How to create Animated Download Button using HTML CSS and JS
    Coding Stella
    • Website

    Related Posts

    JavaScript

    How to Make Subway Super Hopper Game in HTML CSS & JavaScript

    15 February 2026
    JavaScript

    How to Make Memory Unmasked Game in HTML CSS & JavaScript

    4 February 2026
    JavaScript

    How to Make Heart Animation in HTML CSS & JavaScript

    2 February 2026
    Add A Comment
    Leave A Reply Cancel Reply

    Trending Post

    Master Frontend in 100 Days Ebook

    2 March 202432K Views

    How to make Modern Login Form using HTML & CSS | Glassmorphism

    11 January 202431K Views

    How to make I love you Animation in HTML CSS & JavaScript

    14 February 202424K Views

    How to make Valentine’s Day Card using HTML & CSS

    13 February 202415K Views
    Follow Us
    • Instagram
    • Facebook
    • YouTube
    • Twitter
    ads
    Featured Post

    How to make Animated Slider with Button Wave Effect using HTML CSS & JavaScript

    13 September 2025

    How to make Sort the bubble clock using HTML CSS & JavaScript

    20 July 2024

    How to create Flowers Animation using HTML CSS and JS

    11 January 2026

    50 Projects in 50 Days – HTML/CSS and JavaScript

    23 February 2025
    Latest Post

    How to Make Subway Super Hopper Game in HTML CSS & JavaScript

    15 February 2026

    How to make Magic Social Share Menu using HTML CSS and JS

    5 February 2026

    How to Make Memory Unmasked Game in HTML CSS & JavaScript

    4 February 2026

    How to Make Heart Animation in HTML CSS & JavaScript

    2 February 2026
    Facebook X (Twitter) Instagram YouTube
    • About Us
    • Privacy Policy
    • Return and Refund Policy
    • Terms and Conditions
    • Contact Us
    • Buy me a coffee
    © 2026 Coding Stella. Made with 💙 by @coding.stella

    Type above and press Enter to search. Press Esc to cancel.

    Ad Blocker Enabled!
    Ad Blocker Enabled!
    Looks like you're using an ad blocker. We rely on advertising to help fund our site.
    Okay! I understood