20130722

Small, pure Javascript video feedback demonstration

Sorry, your browser does not support canvas.
The demonstration should load (above), on most browsers. You may need to whitelist Javascript for crawlingrobotfortress.blogspot.com to view it. I must admit I'm not 100% certain how to embedd javascript on Blogger posts reliably. Let's see if it works. Mouse over the canvass to adjust the feedback controls.
Here is the source code:
<center>
<canvas height="1" id="feedbackcanvas" width="1"> Sorry, your browser does not support canvas and/or Javascript. </canvas> 

<script type="application/javascript"> 
function feedback(){
    var N = 512;
    var N2 = N/2;
    
    // Initialize canvas data structures
    var feedbackcanvas = document.getElementById('feedbackcanvas');
    feedbackcanvas.width = N;
    feedbackcanvas.height = N;
    var ctx = feedbackcanvas.getContext('2d');
    var imageData = ctx.getImageData(0, 0, N, N);
    
    // Initialize color map
    var colormap = new Uint32Array( 256 );
    for (var i=0; i<256; i++) {
        var hue = 6.0*i/256;
        var r=0;
        var g=0;
        var b=0;
        var C=250;
        if (hue<1) { r+=C; g+=C*hue; } 
        else if (hue<2) { r+=C*(2-hue); g+=C; } 
        else if (hue<3) { g+=C; b+=C*(hue-2); } 
        else if (hue<4) { g+=C*(4-hue); b+=C; } 
        else if (hue<5) { r+=C*(hue-4); b+=C; } 
        else { r+=C; b+=C*(6-hue); } 
        r = Math.round(r);
        g = Math.round(g);
        b = Math.round(b);
        color = 0xff000000 | (r<<16)|(g<<8)|b;
        colormap[i] = color;
    } 
    
    // Compute lookup table map
    var map = new Uint32Array( N*N*2 );
    for (var y = 0; y < N; ++y) {
        for (var x = 0; x < N; ++x) {
            var real = (x-N2)*4.0/N;
            var imag = (y-N2)*4.0/N;
            var real2 = real;
            var imag2 = imag;
            var x2 = Math.round((real2*N/4+N2)*256);
            var y2 = Math.round((imag2*N/4+N2)*256);
            var index = y*N + x;
            map[index*2 ] = x2;
            map[index*2+1] = y2;
        } 
    } 
    var recurrentBuffer1 = new Uint8Array( N*N + 1);
    var recurrentBuffer2 = new Uint8Array( N*N + 1);
    recurrentBuffer1[N*N] = 255;
    recurrentBuffer2[N*N] = 255;
    for (var y = 0; y < N; ++y) 
    for (var x = 0; x < N; ++x) 
    recurrentBuffer1[y*N + x] = x & 0xff;
    offsetx = 64*256;
    offsety = 32*256;
    costheta = 0;
    sintheta = 0;
    function iterate() {
        for (var y = 0; y < N2; ++y) 
        for (var x = 0; x < N2; ++x) {
            var index = y*N + x;
            var x2 = map[index*2 ] - N2*256;
            var y2 = map[index*2+1] - N2*256;
            var x3 = (costheta * x2 + sintheta * y2 >> 8) + N2*256 + offsetx;
            var y3 = (costheta * y2 - sintheta * x2 >> 8) + N2*256 + offsety;
            var xi = x3 >> 8;
            var yi = y3 >> 8;
            var xf = x3 & 0xff;
            var yf = y3 & 0xff;
            var index = xi+yi*N;
            var colorA = recurrentBuffer1[index & 262143];
            var colorB = recurrentBuffer1[index+1 & 262143];
            var colorC = recurrentBuffer1[index+N & 262143];
            var colorD = recurrentBuffer1[index+N+1 & 262143];
            var colorE = colorA*(256-xf)+colorB*xf >> 8;
            var colorF = colorC*(256-xf)+colorD*xf >> 8;
            var color = colorE*(256-yf)+colorF*yf >> 8;
            if (xi>=0 && yi>=0 && xi<n && yi<N) {color += 10;}
            else {color = ~color;} 
            recurrentBuffer2[y*N + x] = color;
            recurrentBuffer2[(N-1-y)*N + x] = color;
            recurrentBuffer2[(N-1-y)*N + (N-1-x)] = color;
            recurrentBuffer2[y*N + (N-1-x)] = color;
        } 
        var temp = recurrentBuffer2;
        recurrentBuffer2 = recurrentBuffer1;
        recurrentBuffer1 = temp;
    } 
    var buf = new ArrayBuffer(imageData.data.length);
    var buf8 = new Uint8ClampedArray(buf);
    var data = new Uint32Array(buf);
    
    // Mouse listener callback
    feedbackcanvas.onmousemove = function(e) {
        var mouseX, mouseY;
        if(e.offsetX) {
            mouseX = e.offsetX;
            mouseY = e.offsetY;
        } 
        else if(e.layerX) {
            mouseX = e.layerX;
            mouseY = e.layerY;
        } 
        mouseX -= N2;
        mouseY -= N2;
        offsetx = mouseX*256;
        offsety = mouseY*256;
        mouseTheta = Math.atan2(mouseX,mouseY);
        mouseRadius = 300;
        costheta = Math.cos(mouseTheta)*mouseRadius;
        sintheta = Math.sin(mouseTheta)*mouseRadius;
    };
    
    function render() {
        iterate();
        for (var y = 0;y < N;++y) {
            for (var x = 0;x < N;++x) {
                var value = recurrentBuffer2[y*N+x];
                data[y*N + x] = colormap[value];
            } 
        } 
        imageData.data.set(buf8);
        ctx.putImageData(imageData, 0, 0);
        setTimeout(render, 10);
    } 
    setTimeout(render, 66);
} 
</script> 
<body onload="feedback()"></body>
</center>