How About That Eh?

Pretty neat, huh? Now try clicking on it. Totally pointless right? Nah it’s kinda nice to look at.

Looks like something you wanna make? Ok lets make it.

Here’s a fiddle if you’d like to follow along.

Step 1: Setup the Environment

Lets start this off in a new .js file, with a standard $(document).ready(); call.

In one of my previous posts, I defined two functions: toCSS() and rcv(). We’re going to reuse that code.

// Take RGB values and return a CSS formatted string representation.
function toCSS(r,g,b){
    return "rgb("+r+","+g+","+b+")";
}

// Return a pseudorandom number between 0 and 255
function rcv(){
    return Math.floor(Math.random()*256);
}

From inside the $(document).ready(); call, set the background to something with a nice dark color that isn’t too sharp. #111 works fine in this case. Make the body the fill the viewport, and make sure all overflow data is hidden.

We’re going to want to make sure to set a size for our tiles, and give them a delay before shunting them out of the screen. 30 pixels is what I used as the default size. I also defined a delay of 3 seconds.

Right now your code should look something like this:

var size = 30;

// Take RGB values and return a CSS formatted string representation.
function toCSS(r,g,b){
    return "rgb("+r+","+g+","+b+")";
}

// Return a pseudorandom number between 0 and 255
function rcv(){
    return Math.floor(Math.random()*256);
}

$(document).ready(function(e){
    // Style up the body.
    $('body').css({
        'width': '100%',
        'height': '100%',
        'background-color': '#111',
        'overflow': 'hidden',
        'position': 'absolute'
    });
}):

Step 2: Place A Tile

It would be nice to have a function with which we could define a tile based on its coordinates, and get some information back for future reference. This function should take the coordinates as arguments and place the tile respectively. However, we’d like to maintain a grid-like structure for tile placement.

To do this, we can take the x,y coordinates and create a placement index based on the floor of the coordinate divided by the size of the tile. That is, we divide the coordinate by the size, and round down. Since this gives us a unique way to identify each tile, it can be used as part of the tile id. Before placing the tile, we should check to make sure one with that identifier doesn’t already exist.

var index_X = Math.floor(x/size);
var index_Y = Math.floor(y/size);
var name = index_X + "_" + index_Y + "_tile";
var exists = document.getElementById(name);
if(exists == null){
    $("body").append("<div id='"+name+"'></div>");
}

Great! This works, except that the tile just appears in its space. It would look nicer if there was some sort of transition animation. Inside the if() statement, add the following lines.

$("#"+name).css({'display': 'none'});
$("#"+name).fadeIn(250).promise();

This will add the element, but won’t immediately show it on the page. It will then take 1/4 of a second to fade in.

At this point, a color can be generated, and all we have to do is modify the css to pretty up the tile.

var color = toCSS(rcv(), rcv(), rcv());
$("#"+name).css({
    'position': 'absolute',
    'background-color': color,
    'box-shadow': '0 0 1px '+color,
    'border-radius': '2px',
    'top': index_Y * size,
    'left': index_X * size,
    'width': size-3,
    'height': size-3
});

Now we can return the name as the identifier. Just like that, the tile has been placed.

Here is the full code for the function.

function gribset(x,y){
    var index_X = Math.floor(x/size);
    var index_Y = Math.floor(y/size);
    var name = index_X + "_" + index_Y + "_cell";
    var exists = document.getElementById(name);
    if(exists == null){
        $("body").append("<div id='"+name+"'></div>");
        $("#"+name).css({'display': 'none'});
        $("#"+name).fadeIn(250).promise();
    }

    var color = toCSS(rcv(), rcv(), rcv());
    $("#"+name).css({
        'position': 'absolute',
        'background-color': color,
        'box-shadow': '0 0 1px '+color,
        'border-radius': '2px',
        'top': index_Y * size,
        'left': index_X * size,
        'width': size-3,
        'height': size-3
    });
    return name;
}

Step 3: Pick a Direction

We’re gonna need one more helper function to determine the direction that each tile should travel. Since this is essentially a random process, we can simply pick a random number from 0-3, and use a series of if statements to determine the information that will be returned.

The function should return the direction of travel (vertical or horizontal aka top or left), and the coordinate point the tile will travel to. Something off the edge of the screen to give the illusion the tile has disappeared.

function goDirection(){
    var num = Math.floor(Math.random()*4);
    if(num==0){
        return ['top', $(document).height()];
    } else if(num==1){
        return ['left', $(document).width()];
    } else if(num==2){
        return ['top', -100];
    } else if(num==3){
        return ['left', -100];
    }
}

Step 4: Shunt!

Great! Now we’re ready to make the tiles move! This is the fun part.

Lets use the function we defined above to get a direction and coordinate point. Lets also randomize the speed at which the tiles move by defining a time interval. Something between 300ms and 900ms.

Once we’ve got that, we can call the animation.

Here is the code.

function shunt(name){
    var d = goDirection();
    var direction = d[0];
    var magnitude = d[1];
    var animation = {};
    var time = Math.floor(Math.random() * 600 + 300);
    animation[direction] = magnitude;
    $("#"+name).animate(animation,time).promise().done(function(){
        $("#"+name).remove();
    });
}

Notice the last line of the function. It calls the animation, then .promise() and .done(). This ensures that the following code will not be executed until the current code has finished processing.

Step 5: Put it All Together

Now that we’ve written all the helper functions we’re going to need, it’s time to write the function that puts them all together.

This function should be able to create a tile, place it, shunt it, then remove it. It should take the coordinate points as arguments, and feed them to the gribset function.

This will place a tile on the page, set its color, and animate its appearance. Then after a brief pause, the function should either make the tile fade out, or shunt if off the page.

Since we’re in the business of randomizing values, we’ll do that with the pause time-interval value as well.

Here’s how my function looks.

function gribble(x,y){
    var name = gribset(x,y);
    var memory = Math.floor(Math.random()*2000+2000)
    setTimeout(function(){
        if(Math.floor(Math.random()*100)%10==0){
            $("#"+name).fadeOut(250).promise().done(function(){
                $("#"+name).remove();
            });
        } else {
            shunt(name);
        }
    }, memory);
}

As you can see, there is a 1/10 chance of the tile disappearing after a time interval between 2 and 4 seconds. If it doesn’t disappear, it gets shunted off the screen.

Step 6: Bring it to Life

We’ve done all the heavy lifting at this point. In order to get the effect seen in the frame at the top of the page, the function should be called after random time intervals, with random coordinates.

function demo_gribble(){
    var x = Math.floor(Math.random()* $(document).width());
    var y = Math.floor(Math.random()* $(document).height());
    var time = Math.floor(Math.random() * 450 + 50);
    gribble(x,y);
    setTimeout(function(){
        demo_gribble();
    }, time);
}

All that’s left is to add a call to this in the $(document).ready(); function. Like so.

$(document).ready(function(e){
    $('body').css({
        'width': '100%',
        'height': '100%',
        'background-color': '#111',
        'overflow': 'hidden',
        'position': 'absolute'
    });

    demo_gribble();
})

Step 7: Click Behavior (Optional)

This part is optional, but it would be nice if the user was able to click on the page to generate a tile at that location. All you have to do is add this to the $(document).ready() function.

$(document).click(function(e){
    var x = e.clientX;
    var y = e.clientY;
    gribble(x, y);
});

And it’s all done. That’s all you need.

Here Is The Full Code

Along with a link to the full-page version

var size = 30;

// Take RGB values and return a CSS formatted string representation.
function toCSS(r,g,b){
    return "rgb("+r+","+g+","+b+")";
}

// Return a pseudorandom number between 0 and 255
function rcv(){
    return Math.floor(Math.random()*256);
}

// Picks a random number from 0 to 3.
// Based on that number, returns a direction
// and the closest point at which the tile would
// be off the screen.
function goDirection(){
    var num = Math.floor(Math.random()*4);
    if(num==0){
        return ['top', $(document).height()];
    } else if(num==1){
        return ['left', $(document).width()];
    } else if(num==2){
        return ['top', -100];
    } else if(num==3){
        return ['left', -100];
    }
}

function gribble(x,y){
    var memory = Math.floor(Math.random()*2000+2000)
    // Create a gribble-tile, and shunt it.
    var name = gribset(x,y);
    // Now pause for a bit before
    setTimeout(function(){
        if(Math.floor(Math.random()*100)%10==0){
            $("#"+name).fadeOut(250).promise().done(function(){
                $("#"+name).remove();
            });
        } else {
            shunt(name);
        }
    }, memory);
}

function gribset(x,y){
    // From the x,y coordinates, grab the nearest
    // grid space and make sure there isn't something
    // in that space already. (We dont need to stack,
    // layers, just change the color of the last one)
    var index_X = Math.floor(x/size);
    var index_Y = Math.floor(y/size);
    var name = index_X + "_" + index_Y + "_cell";
    var exists = document.getElementById(name);
    if(exists == null){
        $("body").append("<div id='"+name+"'></div>");
        $("#"+name).css({'display': 'none'});
        $("#"+name).fadeIn(250).promise();
    }

    // Now add the color, place the tile,
    // and pretty it up with some css.
    var color = toCSS(rcv(), rcv(), rcv());
    $("#"+name).css({
        'position': 'absolute',
        'background-color': color,
        'box-shadow': '0 0 1px '+color,
        'border-radius': '2px',
        'top': index_Y * size,
        'left': index_X * size,
        'width': size-3,
        'height': size-3
    });
    return name;
}

// Pick a direction, and animate the
// movement of the tile to the outside
// of the page. Once that is done, remove it.
function shunt(name){
    var d = goDirection();
    var direction = d[0];
    var magnitude = d[1];
    var animation = {};
    var time = Math.floor(Math.random() * 600 + 300);
    animation[direction] = magnitude;
    $("#"+name).animate(animation,time).promise().done(function(){
        $("#"+name).remove();
    });
}

// A demo function that places a tile at a random
// location at a random time interval.
function demo_gribble(){
    var x = Math.floor(Math.random()* $(document).width());
    var y = Math.floor(Math.random()* $(document).height());
    var time = Math.floor(Math.random() * 450 + 50);
    gribble(x,y);
    setTimeout(function(){
        demo_gribble();
    }, time);
}

$(document).ready(function(e){
    // Style up the body.
    $('body').css({
        'width': '100%',
        'height': '100%',
        'background-color': '#111',
        'overflow': 'hidden',
        'position': 'absolute'
    });

    // Let users click to add a gribble-tile.
    $(document).click(function(e){
        var x = e.clientX;
        var y = e.clientY;
        gribble(x, y);
    });

    // Run the demo function
    demo_gribble();
});

A little bigger than last time, but still not bad at all. Here’s the full fiddle if you want to mess with it.