Cool Ascii Animation using an Image Sprite, Canvas, and Javascript

View code Play Walkthrough

Description

Images can be converted into ascii art by turning the pixels grayscale and representing them using text characters. Dense characters are used for dark pixels and sparse characters are used for light pixels.

In this lesson, HTML5 canvas is used to read the sprite's pixel data which is then converted to grayscale. A character map is then derived from the pixels' grayscale values and injected into the DOM making up the ascii art.

Finally Javascript is used to move the ascii version of the sprite in steps to create the animation.

Add New Comment

14 Comments

(close)
Egypt Urnash

Egypt Urnash

Oh man that long string of ifs in the number-to-character function makes me weep.

Here is something that will give SLIGHTLY different results but is a TON more succinct:

in the variables section:
var greys = “@@WWWW#*:` “;

replace all those “if (gray > n) character = ‘x’” lines with:
character = greys.charAt(Math.floor(gray/25));

This will give you a SLIGHTLY different balance of : versus ‘. If you were really persnickety you could make the ‘greys’ string 51 characters long, and repeat the characters so that you get the exact same results. Or have a much wider set of characters!

LazerFX

LazerFX

Nice article… this HTML5 stuff is really, really cool.

I didn’t like the way things repeated constantly, so I added a couple of new variables (
var waiting = 0;
var rndWait = Math.floor(Math.random()*11);) at the start, and modified the player function to:

if(current_ml == frame_width*(frames-1)*-1 || waiting < rndWait)
if (waiting < rndWait) {
ascii.style.marginLeft = ’0′;
waiting++;
}
else {
waiting = 0;
rndWait = Math.floor(Math.random()*100);
ascii.style.marginLeft = “0″;
}
else
ascii.style.marginLeft = (current_ml – frame_width) + “px”;

And basically, it generates a random number, gets to the end of the animation loop, sets the animation keyframe back to 0 and waits for that randomly set amount of loops, and then starts again. Other options would be to add a minimum and maximum time to wait, playing random frames, etc… but this was good enough for 5 minutes :D

Sonja

Sonja

Thanks for sharing, this is really inspiring.

Unfortunately my asci animation always disappeared (in Firefox) after the first loop and did not reset to marginLeft = 0.

I fixed it by changing the operator here:
if(current_ml == frame_width*(frames-1)*-1)
into:
if(current_ml <= frame_width*(frames-1)*-1)

Sonja

Sonja

less than

Clement

Clement

Sweet! I never imagined it was possible to decompose an image (sprite here) into pixels and into characters (never actually looked on how ascii art was done thought).
Thanks for sharing :)

javad

javad

wow it is best tutorial that i see !!!
thanks. have a good time

pradeep

pradeep

it is not working

Rui

Rui

Why this doesn’t work on IE9+ ?
Even with a polyfill I can’t get it to work…
Anyone?

pradeep

pradeep

to make this work you need to upload to the html,css,js and sprite png to webhosting site..
this is becoz of some security issues with the browsers

Mfarooqi

Mfarooqi

excellent work dear.. I really love it. :) .. i didn’t know how easy is this..

Юра

Юра

Понадобится :)

MelvinTehubijuluw

MelvinTehubijuluw

Great I was looking for this!
I only have one question:
colordata:
I don’t understand why the array length is: W*H*4

It would be great if someone could give me a short explanation of this.

Thanks you!

Richard Mišenčík

Richard Mišenčík

Amazing, wish I would know to code like this :D

gude

gude

it is not working

 
<!-- Lets create an ascii animation -->
<!-- We will use a sprite -->
<img src="http://thecodeplayer.com/uploads/media/sprite.png" id="sprite"/>
<div id="container">
    <!-- The ascii art comes in the pre tag below -->
    <pre id="ascii"></pre>
</div>
 
/*Some styles*/
 
* {margin: 0; padding: 0;}
 
body {
    text-align: center;
}
 
#ascii {
    font-family: monospace;
    font-size: 11px;
    line-height: 70%;
}
 
#sprite {
    display: none;
}
 
#container {
    overflow: hidden;
    display: inline-block;
}
 
window.onload = function(){
    //some variables
    var r, g, b, gray;
    var character, line = "";
    
    //sprite stuff
    var sprite = document.getElementById("sprite");
    var W = sprite.width;
    var H = sprite.height;
    
    //temporary canvas for pixel processing
    var tcanvas = document.createElement("canvas");
    tcanvas.width = W;
    tcanvas.height = H; //same as the image
    var tc = tcanvas.getContext("2d");
    //painting the canvas white before painting the image to deal with pngs
    tc.fillStyle = "white";
    tc.fillRect(0, 0, W, H);
    //drawing the image on the canvas
    tc.drawImage(sprite, 0, 0, W, H);
    
    //accessing pixel data
    var pixels = tc.getImageData(0, 0, W, H);
    var colordata = pixels.data;
    //every pixel gives 4 integers -> r, g, b, a
    //so length of colordata array is W*H*4
    var ascii = document.getElementById("ascii");
    for(var i = 0; i < colordata.length; i = i+4)
    {
        r = colordata[i];
        g = colordata[i+1];
        b = colordata[i+2];
        //converting the pixel into grayscale
        gray = r*0.2126 + g*0.7152 + b*0.0722;
        //overwriting the colordata array with grayscale values
        //colordata[i] = colordata[i+1] = colordata[i+2] = gray;
        
        //text for ascii art.
        //blackish = dense characters like "W", "@"
        //whitish = light characters like "`", "."
        if(gray > 250) character = " "; //almost white
        else if(gray > 230) character = "`";
        else if(gray > 200) character = ":";
        else if(gray > 175) character = "*";
        else if(gray > 150) character = "+";
        else if(gray > 125) character = "#";
        else if(gray > 50) character = "W";
        else character = "@"; //almost black
        
        //newlines and injection into dom
        if(i != 0 && (i/4)%W == 0) //if the pointer reaches end of pixel-line
        {
            ascii.appendChild(document.createTextNode(line));
            //newline
            ascii.appendChild(document.createElement("br"));
            //emptying line for the next row of pixels.
            line = "";
        }
        
        line += character;
    }
    
    //repainting the gray image
    //tc.putImageData(pixels, 0, 0);
    //you can see the grayscale version of the sprite now
    //injecting the canvas into the DOM
    //sprite.parentNode.insertBefore(tcanvas, sprite);
    //you can see the canvas now with the image
    
    //animation time
    var frames = 10; //sprite got 10 frames
    var container = document.getElementById("container");
    //width of container should allow only 1 frame to be seen
    var frame_width = parseInt(window.getComputedStyle(container).width)/frames;
    //window.getComputedStyle is supported in Chrome, FF, Opera, and IE9+
    //The width has "px" at the end so parseInt is used to remove that
    container.style.width = frame_width+"px";
    
    //We will change the margin-left of ascii to move it.
    ascii.style.marginLeft = "0";
    
    setInterval(loop, 1000/10);
    
    function loop()
    {
        var current_ml = parseFloat(ascii.style.marginLeft);
        //if the ascii reaches the last frame(9th in this case)
        //margin needs to be reset to 0
        //frame_width * (10-1) * -1(because we are taking the margin negative)
        if(current_ml == frame_width*(frames-1)*-1)
            ascii.style.marginLeft = "0";
        else
            ascii.style.marginLeft = (current_ml - frame_width) + "px";
    }
    
}

5x 10x 15x 20x

14 Comments

Description