HTML5: Amazing Visual Animation Effects

Chrome rendering the visualisation algorithm.
HTML5 is very powerful if you can think laterally about how to pass processing off from the CPU to the GPU. Here I show a simple trick which produces shockingly complex visuals as part of the Sonic Field project.

The normal way that one finds for manipulating images at the pixel level in HTML5 is the get/putImageData methods on the canvas element. The problem with doing any serious pixel manipulation with these methods is that you need to loop over the pixels in javascript loops on the CPU. In other words, you have a GPU and you are using a low performance language on the CPU to do all the work! How about not doing it that way? What canvas will do is scale using the GPU. However, it will not scale pixel data. But there is a way around this; pixel data can be copied form one canvas to another and then the second canvas added to the first as an image. This latter operation is scaled (transformed, alpha channelled, rotated and sheared) using the optimal path for the computer as the browser sees fit. This means your script does not need to do very much to enable some serious processing.

Here is a video demonstrating the code working [I go on to show how it works in script lower down the page]:

http://youtu.be/AZtFf3_K3g8?hd=1



Below is the script which produced this effect. It uses the requestAnimationFrame method which synchronises repaints with the machine's refresh rate and so stops the script from exhausting the computer's resources. It also uses two canvases, only one of them is visible. The trick is here:


 hContext.putImageData(imgD1,0,0);
 context.scale(scaleFactor,scaleFactor);
 var sx=canvas.width*scale/2;
        var sy=canvas.height*scale/2;
        context.translate(-sx+1.6,-sy+1.1)
 context.globalAlpha=0.1;
 context.drawImage(hCanvas,0,0);
 
        context.globalAlpha=1.0;


I take what ever is on the canvas and copy it to the invisible canvas. I then set the scale on the canvas and put the image back over the top of the original but scale up a bit. This makes the image slowly move to the edges and blend with its self. The constant change is then added by rendering different sized and coloured circles in the middle of the canvas.

To turn this into a true visualizer, I will use a web socket (or similar) to push even/time information from the server to the script which will then alter the size,colour and shape of the drawing in the center. But time is limited so this is far as I have gotten today!

<!DOCTYPE html >
<html>
<head>
<style type="text/css">
canvas
{
    border: 0
    color: black;
    background: black;
    display: block;
}

canvas#hiddenArea
{
    display: none;
}
div
{
    text-align:center;
}
body
{
    padding:0;
    margin:0;
    color: white;
    background:black;
}
</style>
<script type="text/javascript">
var context;
var canvas;
var hCanvas;
var hContext;

(function() {
})();
function startAnimation(){
    var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
    window.requestAnimationFrame = requestAnimationFrame;
 setup();
 repaint();
}

function repaint(){
 var imgD1=context.getImageData(0,0,canvas.width,canvas.height);
 var scale=0.05
 var scaleFactor=1+scale;
 hContext.putImageData(imgD1,0,0);
 context.scale(scaleFactor,scaleFactor);
 var sx=canvas.width*scale/2;
    var sy=canvas.height*scale/2;
    context.translate(-sx+1.6,-sy+1.1)
 context.globalAlpha=0.1;
 context.drawImage(hCanvas,0,0);
 
    context.globalAlpha=1.0;
    if((Math.random()*20)>19){
        context.lineWidth = 2;
        var c="rgb(" + Math.floor(Math.random()*255) + "," + Math.floor(Math.random()*255) + ","+ Math.floor(Math.random()*255) + ")";
        context.strokeStyle = c;
    }
 var radius=Math.random()*50;
    context.setTransform(1,0,0,1,0,0);
    context.beginPath();
    context.arc(canvas.width/2,canvas.height/2,radius,0,2*Math.PI,true);
    context.stroke();
    requestAnimationFrame(repaint);
}

function setup() {
  canvas=document.getElementById('drawArea');
  hCanvas=document.getElementById('hiddenArea');
  if (canvas.getContext) {
    context=canvas.getContext('2d');
    hContext=hCanvas.getContext('2d');
 context.globalAlpha =1;
    context.strokeStyle = "rgb(150,29,28)";
    //context.strokeStyle = 'blue';
  } else {
    // put code for browsers that don’t support canvas here
    alert("This page uses HTML 5 to render correctly. Other browsers may not see anything.");
  }
}
</script>
</head>
<body onload="startAnimation()">
<canvas id="drawArea" width="1280" height="720"></canvas>
<canvas id="hiddenArea" width="1280" height="720"></canvas>
</body>
</html>
cat