← Mike Higgins Home
Custom Code Experiment •

Chromatophores 1

Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html>
<head>
    <style>
        html, body {
            overflow: hidden;
            background: #000;
            padding: 0px;
            margin: 0px;
            width: 100%;
            height: 100%;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        // Initialize canvas and context
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");

        // Set initial dimensions
        var w = canvas.width = window.innerWidth;
        var h = canvas.height = window.innerHeight;

        // Handle window resize
        window.onresize = function() {
            w = canvas.width = window.innerWidth;
            h = canvas.height = window.innerHeight;
        };

        // Configuration - same as your original code
        var pixels = [];
        var emitterCount = 3;
        var pixelSize = 6;
        var gap = 2;
        var lineWidth = 1;
        var maxLifetime = 3000;
        var minLifetime = 1000;
        var spawnRate = 500;
        var shape = "rect"; // rect/arc
        var style = "stroke"; // fill/stroke
        var opacity = 0.2;
        var hue = 0; // Starting Hue (0-360)
        var hueRange = 50; // Hue +/-

        // Create new pixels - EXACTLY as in your original code
        function emit() {
            for(var i = 0; i < emitterCount; i++) {
                pixels.push({
                    x: Math.ceil((Math.random() * w) / (pixelSize + gap)) * (pixelSize + gap),
                    y: Math.ceil((Math.random() * h) / (pixelSize + gap)) * (pixelSize + gap),
                    t: Math.random() * (maxLifetime - minLifetime) + minLifetime,
                    s: Date.now(),
                    h: Math.random() * ((hue + hueRange) - (hue - hueRange)) + (hue - hueRange)
                });
            }
        }

        // Adjust gap for stroke style - exactly as in your code
        if(style == "stroke") {
            gap *= 2;
        }

        // Draw all pixels - exactly as in your code
        function draw() {
            for(var i = 0; i < pixels.length; i++) {
                ctx.beginPath();
                ctx.globalAlpha = opacity;
                ctx.lineWidth = lineWidth;
                var diff = (Date.now() - pixels[i].s) / pixels[i].t;
                var perc = Math.round(diff * 100);
                ctx.strokeStyle = "hsl(" + pixels[i].h + "," + perc + "%,50%)";
                ctx.fillStyle = "hsl(" + pixels[i].h + "," + perc + "%,50%)";

                if(shape == "rect") {
                    ctx.rect(pixels[i].x, pixels[i].y, pixelSize, pixelSize);
                } else if(shape == "arc") {
                    ctx.arc(pixels[i].x, pixels[i].y, pixelSize, 0, 2 * Math.PI);
                }

                if(style == "stroke") {
                    ctx.stroke();
                } else if(style == "fill") {
                    ctx.fill();
                }
                ctx.closePath();
            }
        }

        // Animate pixels (move them) - exactly as in your code
        function animate() {
            for(var i = 0; i < pixels.length; i++) {
                if(pixels[i].s + pixels[i].t < Date.now()) {
                    pixels.splice(i, 1);
                } else {
                    var dir = Math.round(Math.random() * 8) % 8;
                    switch(dir) {
                        case 0: // N
                            pixels[i].y -= gap + pixelSize;
                            break;
                        case 1: // NE
                            pixels[i].x += gap + pixelSize;
                            pixels[i].y -= gap + pixelSize;
                            break;
                        case 2: // E
                            pixels[i].x += gap + pixelSize;
                            break;
                        case 3: // SE
                            pixels[i].x += gap + pixelSize;
                            pixels[i].y += gap + pixelSize;
                            break;
                        case 4: // S
                            pixels[i].y += gap + pixelSize;
                            break;
                        case 5: // SW
                            pixels[i].x -= gap + pixelSize;
                            pixels[i].y += gap + pixelSize;
                            break;
                        case 6: // W
                            pixels[i].x -= gap + pixelSize;
                            break;
                        case 7: // NW
                            pixels[i].x -= gap + pixelSize;
                            pixels[i].y -= gap + pixelSize;
                            break;
                    }
                }
            }
        }

        // Main render loop
        function render() {
            // DO NOT clear the canvas - let pixels accumulate (as in your original code)
            draw();
            animate();
            requestAnimationFrame(render);
        }

        // Start immediately - no need to wait for window.onload
        emit();
        setInterval(emit, spawnRate);
        requestAnimationFrame(render);

        // Remove the click handler that clears the canvas
        // canvas.onclick = function() {
        //     ctx.clearRect(0, 0, w, h);
        // }
    </script>
</body>
</html>