don't worry, it's probably fine

Building my own tixy.land

10 Nov 2020

nablopomo html5 canvas

Earlier this week I saw tixy on my Twitter feed.

It’s an interactive graphic, composed of a grid of circles, that you can customise with your own “tixy” function. The graphic evolves over time using this function.

The name is something like an acronym, as the function takes four arguments:

  • t - time since the function last changed, constantly increasing
  • i - index of the circle in the grid
  • x - x-coordinate of the circle
  • y - y-coordinate of the circle.

I had a lot of fun playing with this so I decided to reverse engineer and build my own tixy, looking at the original code as little as possible.

Bear in mind my front-end skills, especially Javascript, could charitably be described as “shocking” or “why even try”.

Creating a basic drawing, no animation

I’ve never used the HTML5 Canvas API before so now seemed as good a time as any.

What I needed was a square grid, with an equal number of circles on each side. After digging through a few examples and documentation, I came up with this:

<canvas id="tixy"></canvas>
<script type="text/javascript">

    // Apparently you can directly reference elements by
    // their id rather than call document.getElementById('tixy')
    // which is neat?
    var context = tixy.getContext("2d");

    var size = 500;
    var dimensions = 16;
    
    context.canvas.width  = size;
    context.canvas.height = size;

    // Set the background as black
    context.fillStyle = 'black';
    context.fillRect(0,0,size,size);
    
    context.fillStyle = 'white';

    // Create some circles!
    for (i = 0; i <= dimensions; i++) {
        for (j = 0; j <= dimensions; j++) {

            // This adjustment is so we get a whole circle on the edges
            // rather than semi-circles
            var x = (i+0.5)*(size/dimensions);
            var y = (j+0.5)*(size/dimensions);
            var radius = (size/(2*dimensions));

            context.beginPath();
            context.arc(x, y, radius, 0, 2 * Math.PI);
            context.fill();
        }
    }
</script>

Animating the grid

To add some animations I added a function, document.tixy, that I could repeatedly call.

<canvas id="tixy"></canvas>
<script type="text/javascript">
    document.tixy = (t,i,x,y) => (Math.random()-0.5)*2;

    var context = tixy.getContext("2d");
    var size = 500;
    var dimensions = 16;

    context.canvas.width  = size;
    context.canvas.height = size;

    // Note the start time of the pattern
    document.initialTime = new Date() / 1000;

    function render() {
        
        // Blank out the background
        context.fillStyle = 'black';
        context.fillRect(0,0,size,size);

        var time = new Date() / 1000;

        for (i = 0; i <= dimensions; i++) {
            for (j = 0; j <= dimensions; j++) {

                var x = (i+0.5)*(size/dimensions);
                var y = (j+0.5)*(size/dimensions);

                // Calculate the tixy value
                var tixyValue = document.tixy(
                    time - document.initialTime, i*dimensions + j, i, j
                );

                // The absolute tixy value determines the size of the circle
                var radius = (size/(2*dimensions))*Math.min(1, Math.abs(tixyValue));

                // The sign of the tixy value determines the colour of the circle
                if (tixyValue > 0) {
                    context.fillStyle = 'white';
                } else if (tixyValue < 0) {
                    context.fillStyle = 'red';
                } else {
                    context.fillStyle = 'black';
                }

                context.beginPath();
                context.arc(x, y, radius, 0, 2 * Math.PI);
                context.fill();
            }
        }

        // Run this function again in 50ms
        setTimeout(render, 50);
    }
    
    // Start the rendering process
    render();
</script>

And hurrah, it works!

Other stuff

I added a <textarea> that would read and update the document.tixy function, and could experiment with it a bit more, but that’s left as an exercise for the reader :)


November is National Blog Posting Month, or NaBloPoMo. I’ll be endeavouring to write one blog post per day in the month of November 2020 - some short and sweet, others long and boring.