library / backgrounds / truchet-tiles
live - 60fps 1920 x 1080
truchet-tiles.html - self-contained
<!DOCTYPE html>
<html>
<head>
<title>Truchet Tiles</title>
<style>
    :root { --bg-color: #0a0a12; --line-color: #f5c542; --line-2: #ef476f; }
    * { margin: 0; padding: 0; }
    body { height: 100vh; background: var(--bg-color); overflow: hidden; }
    canvas { display: block; }
</style>
</head>
<body>
    <canvas id="c"></canvas>
<script>
    const cv = document.getElementById('c'), ctx = cv.getContext('2d');
    const css = getComputedStyle(document.documentElement);
    const bg = (css.getPropertyValue('--bg-color') || '#0a0a12').trim();
    const l1 = (css.getPropertyValue('--line-color') || '#f5c542').trim();
    const l2 = (css.getPropertyValue('--line-2') || '#ef476f').trim();
    const T = 56; let w, h, cols, rows;
    function build() { w = cv.width = innerWidth; h = cv.height = innerHeight; cols = Math.ceil(w / T) + 1; rows = Math.ceil(h / T) + 1; }
    addEventListener('resize', build); build();
    // smooth noise for evolving tile orientation
    function hash(x, y) { let n = Math.sin(x * 127.1 + y * 311.7) * 43758.5453; return n - Math.floor(n); }
    function step(t) {
        ctx.fillStyle = bg; ctx.fillRect(0, 0, w, h);
        ctx.lineWidth = 5; ctx.lineCap = 'round';
        for (let j = 0; j < rows; j++) {
            for (let i = 0; i < cols; i++) {
                const x = i * T, y = j * T, r = T / 2;
                const flip = Math.sin(hash(i, j) * 6.28 + t * 0.6) > 0;
                ctx.strokeStyle = (i + j) % 2 ? l1 : l2;
                ctx.beginPath();
                if (flip) {
                    ctx.arc(x, y, r, 0, Math.PI / 2);
                    ctx.arc(x + T, y + T, r, Math.PI, Math.PI * 1.5);
                } else {
                    ctx.arc(x + T, y, r, Math.PI / 2, Math.PI);
                    ctx.arc(x, y + T, r, Math.PI * 1.5, Math.PI * 2);
                }
                ctx.stroke();
            }
        }
    }
    if (matchMedia('(prefers-reduced-motion: reduce)').matches) step(1.0);
    else { let t = 0; (function loop(){ t += 0.02; step(t); requestAnimationFrame(loop); })(); }
</script>
</body>
</html>

About this animation

Interlocking arcs tile into an ever-shifting woven maze, connections forming and reforming across the grid in two-tone neon.

Source Code
Copied to clipboard