library / text & typography / kinetic-typography-swarm
live - 60fps 1920 x 1080
kinetic-typography-swarm.html - self-contained
<!DOCTYPE html>
<html>
<head>
<title>Kinetic Typography Swarm</title>
<style>
    body {
        background-color: #1a1a1a;
        color: white;
        margin: 0;
        overflow: hidden;
        font-family: 'Courier New', Courier, monospace;
        display: flex;
        justify-content: center;
        align-items: center;
        text-align: center;
        height: 100vh;
    }
    canvas {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 1;
    }
    h1 {
        z-index: 2;
        font-size: 4rem;
        color: #fff;
    }
</style>
</head>
<body>
    <h1>KINETIC</h1>
    <canvas id="swarmCanvas"></canvas>
<script>
    const canvas = document.getElementById('swarmCanvas');
    const ctx = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    let particles = [];
    const chars = ['$', '#', '@', '&', '%', '}', '{', ']', '['];
    const mouse = {
        x: undefined,
        y: undefined,
    };
    window.addEventListener('mousemove', (event) => {
        mouse.x = event.clientX;
        mouse.y = event.clientY;
    });
    class Particle {
        constructor(x, y, char, color, size) {
            this.x = x;
            this.y = y;
            this.char = char;
            this.color = color;
            this.size = size;
            this.baseX = this.x;
            this.baseY = this.y;
            this.density = (Math.random() * 30) + 1;
        }
        draw() {
            ctx.fillStyle = this.color;
            ctx.font = this.size + 'px Courier New';
            ctx.fillText(this.char, this.x, this.y);
        }
        update() {
            let dx = mouse.x - this.x;
            let dy = mouse.y - this.y;
            let distance = Math.sqrt(dx * dx + dy * dy);
            let forceDirectionX = dx / distance;
            let forceDirectionY = dy / distance;
            let maxDistance = 200;
            let force = (maxDistance - distance) / maxDistance;
            if (distance < maxDistance) {
                this.x -= forceDirectionX * this.density * 0.5;
                this.y -= forceDirectionY * this.density * 0.5;
            } else {
                if (this.x !== this.baseX) {
                    let dx = this.x - this.baseX;
                    this.x -= dx / 10;
                }
                if (this.y !== this.baseY) {
                    let dy = this.y - this.baseY;
                    this.y -= dy / 10;
                }
            }
        }
    }
    function init() {
        particles = [];
        for (let i = 0; i < 1000; i++) {
            let x = Math.random() * canvas.width;
            let y = Math.random() * canvas.height;
            let char = chars[Math.floor(Math.random() * chars.length)];
            let color = 'white';
            let size = Math.random() * 15 + 5;
            particles.push(new Particle(x, y, char, color, size));
        }
    }
    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        for (let i = 0; i < particles.length; i++) {
            particles[i].update();
            particles[i].draw();
        }
        requestAnimationFrame(animate);
    }
    init();
    animate();
    window.addEventListener('resize', function(){
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        init();
    });
</script>
</body>
</html>

About this animation

Letters swarming and forming shapes.

Under the hood

Text-to-Particle Canvas Mapping

This highly advanced effect first draws a large string of text onto a hidden off-screen canvas. It then scans the canvas data using getImageData() to find non-transparent pixels. Particles are then spawned at those exact pixel locations, forming the shape of the words before exploding interactively.

Source Code
Copied to clipboard