<canvas></canvas>
body {
    margin: 0;
    overflow: hidden;
}

canvas {
    image-rendering: pixelated;
}
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');

const size = 25;
const speed = 2;
const color = '#2e246d';
let entities = [];

const update = () => {
    context.clearRect(0, 0, canvas.width, canvas.height);
    
    context.fillStyle = color;
    context.strokeStyle = color;
    
    for (const entity of entities) {
        context.save();
        context.translate(entity.position.x, entity.position.y);
        context.rotate(entity.angle);
        
        context.beginPath();
        context.rect(size / -2, size / -2, size, size);
        context.closePath();
        if (entity.isFilled) {
            context.fill();
        } else {
            context.stroke();
        }
        
        context.restore();
        
        entity.position.x += entity.velocity.x;
        entity.position.y += entity.velocity.y;
        
        if (entity.position.x < size * -2) {
            entity.position.x = canvas.width + size * 2;
        }
        
        if (entity.position.x > canvas.width + size * 2) {
            entity.position.x = size * -2;
        }
        
        if (entity.position.y < size * -2) {
            entity.position.y = canvas.height + size * 2;
        }
        
        if (entity.position.y > canvas.height + size * 2) {
            entity.position.y = size * -2;
        }
    }
    
    requestAnimationFrame(update);
};

const generate = () => {
    const count = Math.floor((canvas.width * canvas.height) ** (1 / 3.5));
    
    entities = [];
    for (let index = 0; index < count; index++) {
        entities.push({
            position: {
                x: Math.random() * canvas.width,
                y: Math.random() * canvas.height
            },
            velocity: {
                x: (Math.random() - 0.5) * 2 * speed,
                y: (Math.random() - 0.5) * 2 * speed
            },
            angle: Math.random() * Math.PI * 2,
            isFilled: Math.random() < 0.1
        });
    }
};

const resize = () => {
    [canvas.width, canvas.height] = [innerWidth, innerHeight];
    
    generate();
};

const init = () => {
    resize();
    
    requestAnimationFrame(update);
};

window.addEventListener('load', init);
window.addEventListener('resize', resize);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.