"video2ascii cube by p.irish"
Bootstrap 3.3.0 Snippet by rayrc

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <!DOCTYPE html> <head> <title>video2ascii in a cube!</title> <!-- Tab Atkins created the video to ascii demo for the HTML5 Meetup of the Silicon Valley Google Technology User Group on July 7th, 2010: http://www.xanthir.com/video/demo3.html Brendan Kenny provided some matrix mathematics to project a 3D cube in 2D space Then I added some form controls to add some interactivity. ~ Paul Irish --> <style> html, body, div { margin: 0; padding: 0; } html, body { height: 100%; width: 100%; display: block; } body { font-size: 9px; color: black; background-color: #FAFFF5; background-image: -webkit-gradient(radial, 500 300, 400, 500 300, 40, from(#FAFFF5), to(#aaaaaa)); -webkit-user-select: none; -moz-user-select: none; user-select: none;} aside { display: block; position: absolute; top: 20px; left: 20px; font-family: sans-serif; color: #555; } div { position: absolute; font-family: monospace; line-height: 1em; white-space: pre; left: 400px; top: 200px; } #letter { position: absolute; left: -9001px; font-family: monospace; line-height: 1em; } h1 { font-size: 17px; } p { font-size: 13px; } label, ul { font-size: 14px; } canvas { position: absolute; left: -9001px; } .fadey #top { -webkit-mask-image: -webkit-gradient(linear, 0% 100%, 0% 0%, from(transparent), color-stop(0.2, transparent), to(white)); } .fadey #right { -webkit-mask-image: -webkit-gradient(linear, 100% 100%, 0% 100%, from(white), color-stop(0.8, transparent), to(transparent)); } .fadey #bottom { -webkit-mask-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), color-stop(0.2, transparent), to(white)); } .fadey #left { -webkit-mask-image: -webkit-gradient(linear, 0% 100%, 100% 100%, from(white), color-stop(0.8, transparent), to(transparent)); } #cube { position: absolute; width: 0; height: 0; top: 290px; left: 500px;} .face { position: absolute; overflow: hidden;} .face:not(#back) { pointer-events: none; } fieldset { border: 0; height: 20px; display: block; line-height: 14px; padding: 4px; } .no3d #persp { display: none; } input, label { margin: 3px; padding: 1px; } #hey { opacity: 0; position: absolute; top: 445px; left: 300px; width: 360px; text-align: center; font-size: 16px; -moz-transition: opacity 0.6s ease-out; -o-transition: opacity 0.6s ease-out; -webkit-transition: opacity 0.6s ease-out; transition: opacity 0.6s ease-out; } .unplayed #hey { opacity: 1; } </style> </head> <body class="unplayed"> <aside> <fieldset> <label for="res">Adjust resolution:</label> <input type="range" min="2" max="13" id="res" value="8"> </fieldset> <fieldset id="perpectiveAdj"> <label for="persp">Adjust perspective:</label> <input type="range" min="0" max="100" id="persp" value="0"> </fieldset> <fieldset> <label for="fadey">Fade the edges: </label> <input type="checkbox" id="fadey"> </fieldset> </aside> <aside id="hey"> Hit play. Drag the video around. </aside> <div id='cube'> <div class="face" id="left"></div> <div class="face" id="top"></div> <div class="face" id="right"></div> <div class="face" id="bottom"></div> <video controls="true" loop="true" class="face" id="back" onended="this.play()"> <source src="http://onlyhd.co.s3-us-west-1.amazonaws.com/video/milky.mp4" type='video/mp4' /> <source src="http://onlyhd.co.s3-us-west-1.amazonaws.com/video/milky.webm" type='video/webm' /> </video> </div> <canvas id='canvs'></canvas> <!-- offscreen canvas used to interpret brightness values --> <span id="letter">o</span> <!-- relative size of font determined by size of this offscreen o --> <script> /** * this script handles creating the cube and its manipulation * it's all by brendan kenny */ var Matrix4x4 = function() { this.identity(); }; Matrix4x4.prototype.identity = function() { this.m11 = 1; this.m12 = 0; this.m13 = 0; this.m14 = 0; this.m21 = 0; this.m22 = 1; this.m23 = 0; this.m24 = 0; this.m31 = 0; this.m32 = 0; this.m33 = 1; this.m34 = 0; this.m41 = 0; this.m42 = 0; this.m43 = 0; this.m44 = 1; return this; }; Matrix4x4.prototype.copy = function(src) { this.m11 = src.m11; this.m12 = src.m12; this.m13 = src.m13; this.m14 = src.m14; this.m21 = src.m21; this.m22 = src.m22; this.m23 = src.m23; this.m24 = src.m24; this.m31 = src.m31; this.m32 = src.m32; this.m33 = src.m33; this.m34 = src.m34; this.m41 = src.m41; this.m42 = src.m42; this.m43 = src.m43; this.m44 = src.m44; return this; }; Matrix4x4.prototype.multiply = function(view, local) { this.m11 = view.m11*local.m11 + view.m12*local.m21 + view.m13*local.m31 + view.m14*local.m41; this.m12 = view.m11*local.m12 + view.m12*local.m22 + view.m13*local.m32 + view.m14*local.m42; this.m13 = view.m11*local.m13 + view.m12*local.m23 + view.m13*local.m33 + view.m14*local.m43; this.m14 = view.m11*local.m14 + view.m12*local.m24 + view.m13*local.m34 + view.m14*local.m44; this.m21 = view.m21*local.m11 + view.m22*local.m21 + view.m23*local.m31 + view.m24*local.m41; this.m22 = view.m21*local.m12 + view.m22*local.m22 + view.m23*local.m32 + view.m24*local.m42; this.m23 = view.m21*local.m13 + view.m22*local.m23 + view.m23*local.m33 + view.m24*local.m43; this.m24 = view.m21*local.m14 + view.m22*local.m24 + view.m23*local.m34 + view.m24*local.m44; this.m31 = view.m31*local.m11 + view.m32*local.m21 + view.m33*local.m31 + view.m34*local.m41; this.m32 = view.m31*local.m12 + view.m32*local.m22 + view.m33*local.m32 + view.m34*local.m42; this.m33 = view.m31*local.m13 + view.m32*local.m23 + view.m33*local.m33 + view.m34*local.m43; this.m34 = view.m31*local.m14 + view.m32*local.m24 + view.m33*local.m34 + view.m34*local.m44; this.m41 = view.m41*local.m11 + view.m42*local.m21 + view.m43*local.m31 + view.m44*local.m41; this.m42 = view.m41*local.m12 + view.m42*local.m22 + view.m43*local.m32 + view.m44*local.m42; this.m43 = view.m41*local.m13 + view.m42*local.m23 + view.m43*local.m33 + view.m44*local.m43; this.m44 = view.m41*local.m14 + view.m42*local.m24 + view.m43*local.m34 + view.m44*local.m44; return this; }; Matrix4x4.prototype.rotateX = function(theta) { var cos = Math.cos(theta), sin = Math.sin(theta), c2, c3; c2 = cos*this.m12 + this.m13*sin; c3 = cos*this.m13 - this.m12*sin; this.m12 = c2; this.m13 = c3; c2 = cos*this.m22 + this.m23*sin; c3 = cos*this.m23 - this.m22*sin; this.m22 = c2; this.m23 = c3; c2 = cos*this.m32 + this.m33*sin; c3 = cos*this.m33 - this.m32*sin; this.m32 = c2; this.m33 = c3; c2 = cos*this.m42 + this.m43*sin; c3 = cos*this.m43 - this.m42*sin; this.m42 = c2; this.m43 = c3; return this; }; Matrix4x4.prototype.rotateY = function(theta) { var cos = Math.cos(theta), sin = Math.sin(theta), c1, c3; c1 = cos*this.m11 - this.m13*sin; c3 = cos*this.m13 + this.m11*sin; this.m11 = c1; this.m13 = c3; c1 = cos*this.m21 - this.m23*sin; c3 = cos*this.m23 + this.m21*sin; this.m21 = c1; this.m23 = c3; c1 = cos*this.m31 - this.m33*sin; c3 = cos*this.m33 + this.m31*sin; this.m31 = c1; this.m33 = c3; c1 = cos*this.m41 - this.m43*sin; c3 = cos*this.m43 + this.m41*sin; this.m41 = c1; this.m43 = c3; return this; }; Matrix4x4.prototype.rotateZ = function(theta) { var cos = Math.cos(theta), sin = Math.sin(theta), c1, c2; c1 = cos*this.m11 + this.m12*sin; c2 = cos*this.m12 - this.m11*sin; this.m11 = c1; this.m12 = c2; c1 = cos*this.m21 + this.m22*sin; c2 = cos*this.m22 - this.m21*sin; this.m21 = c1; this.m22 = c2; c1 = cos*this.m31 + this.m32*sin; c2 = cos*this.m32 - this.m31*sin; this.m31 = c1; this.m32 = c2; c1 = cos*this.m41 + this.m42*sin; c2 = cos*this.m42 - this.m41*sin; this.m41 = c1; this.m42 = c2; return this; }; Matrix4x4.prototype.translate = function(tx, ty, tz) { this.m14 += this.m11*tx + this.m12*ty + this.m13*tz; this.m24 += this.m21*tx + this.m22*ty + this.m23*tz; this.m34 += this.m31*tx + this.m32*ty + this.m33*tz; this.m44 += this.m41*tx + this.m42*ty + this.m43*tz; return this; }; // taken from or based on relevant parts of Modernizr var testr = (function() { var ret = {}, props2d = ['transform', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform'], props3d = ['perspective', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective'], elem = document.createElement('div'), e_style = elem.style, testMediaQuery = function(mq) { var st = document.createElement('style'), ret; st.textContent = mq + '{#modernizr{height:3px}}'; (document.head || document.getElementsByTagName('head')[0]).appendChild(st); elem.id = 'modernizr'; document.documentElement.appendChild(elem); ret = elem.offsetHeight === 3; st.parentNode.removeChild(st); elem.parentNode.removeChild(elem); return !!ret; }; ret.transform2d = false; ret.transform2dProp = ''; // 2d transform support for (var i = 0; i < props2d.length; i++){ if (e_style[props2d[i]] !== undefined) { ret.transform2dProp = props2d[i]; ret.transform2d = true; break; } } // test whether a unit is needed for translation entries in // transformation matrix. firefox currently needs this. if (ret.transform2d) { e_style[ret.transform2dProp] = 'matrix(1,0,0,1,1,1)'; ret.translateUnit = e_style[ret.transform2dProp].indexOf('matrix') === -1; } // 3d transform support ret.transform3d = false; ret.perspectiveProp = ''; for (var i = 0; i < props3d.length; i++){ if (e_style[props3d[i]] !== undefined) { ret.perspectiveProp = props3d[i]; ret.transform3d = true; break; } } // double check 3d on webkit if (ret.transform3d && 'webkitPerspective' in document.documentElement.style) { ret.transform3d = testMediaQuery('@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-ms-transform-3d),(-webkit-transform-3d),(modernizr)'); } return ret; })(); function makeCubeGo() { // if no transform property, just give up now if (!testr.transform2d) { return; } var leftMat = new Matrix4x4(), topMat = new Matrix4x4(), rightMat = new Matrix4x4(), bottomMat = new Matrix4x4(), backMat = new Matrix4x4(), cubeMat = new Matrix4x4(), tmpMat = new Matrix4x4(), leftDiv = document.getElementById('left'), topDiv = document.getElementById('top'), rightDiv = document.getElementById('right'), bottomDiv = document.getElementById('bottom'), backDiv = document.getElementById('back'), translateUnit = testr.translateUnit ? 'px' : '', // use 3d transforms when available transformFace = testr.transform3d ? transformFace3d : transformFace2d; // center faces on origin of parent function setFaceSize(faceDiv, w, h) { faceDiv.style.width = w + 'px'; faceDiv.style.left = -w/2 + 'px'; faceDiv.style.height = h + 'px'; faceDiv.style.top = -h/2 + 'px'; } // take 3d matrix and set 2d version on a face function transformFace2d(faceDiv, matrix, isBackFace) { var faceStyle = faceDiv.style, // scientific notation hurts us, precious str = 'matrix(' + matrix.m11.toFixed(10) + ',' + matrix.m21.toFixed(10) + ',' + matrix.m12.toFixed(10) + ',' + matrix.m22.toFixed(10) + ',' + matrix.m14.toFixed(10) + translateUnit + ',' + matrix.m24.toFixed(10) + translateUnit + ')'; faceStyle[testr.transform2dProp] = str; // z-index based on depth of middle of face for proper stacking faceStyle.zIndex = ~~(matrix.m34 * 10 + 4000); } // take 3d matrix and set on a face function transformFace3d(faceDiv, matrix, isBackFace) { var faceStyle = faceDiv.style, // scientific notation hurts us, precious // units not needed in matrix3d, but may change in the future // it may be faster to construnct a WebKitCSSMatrix and call toString() str = 'matrix3d(' + matrix.m11.toFixed(10) + ',' + matrix.m21.toFixed(10) + ',' + matrix.m31.toFixed(10) + ',' + matrix.m41.toFixed(10) + ',' + matrix.m12.toFixed(10) + ',' + matrix.m22.toFixed(10) + ',' + matrix.m32.toFixed(10) + ',' + matrix.m42.toFixed(10) + ',' + matrix.m13.toFixed(10) + ',' + matrix.m23.toFixed(10) + ',' + matrix.m33.toFixed(10) + ',' + matrix.m43.toFixed(10) + ',' + matrix.m14.toFixed(10) + ',' + matrix.m24.toFixed(10) + ',' + matrix.m34.toFixed(10) + ',' + matrix.m44.toFixed(10) + ')'; faceStyle[testr.transform2dProp] = str; } // write current transform state to elements function writeTransforms() { tmpMat.multiply(cubeMat, leftMat); transformFace(leftDiv, tmpMat); tmpMat.multiply(cubeMat, backMat); transformFace(backDiv, tmpMat, true); tmpMat.multiply(cubeMat, rightMat); transformFace(rightDiv, tmpMat); tmpMat.multiply(cubeMat, topMat); transformFace(topDiv, tmpMat); tmpMat.multiply(cubeMat, bottomMat); transformFace(bottomDiv, tmpMat); } var dragging, lastX, lastY, angleX = 0, angleY = 0; function dragStart(event) { if (event.target.tagName != 'INPUT') { dragging = true; lastX = event.clientX; lastY = event.clientY; } } function dragMove(event) { if (dragging) { var x = event.clientX, y = event.clientY, deltaX = lastX - x, deltaY = y - lastY; // made up hacky scale factors angleX -= deltaX * Math.PI / 900; angleY -= deltaY * Math.PI / 900; cubeMat.identity().rotateY(angleX).rotateX(angleY); writeTransforms(); lastX = x; lastY = y; } } function dragEnd(event) { dragging = false; } // set cube's size at any time (exposed on window below) function setCubeSize(width, height, depth) { setFaceSize(leftDiv, depth, height); setFaceSize(backDiv, width, height); setFaceSize(rightDiv, depth, height); setFaceSize(topDiv, width, depth); setFaceSize(bottomDiv, width, depth); var halfPi = Math.PI / 2; leftMat.identity().translate(-width/2, 0, depth/2).rotateY(-halfPi); backMat.identity().translate(0, 0, 0); rightMat.identity().translate(width/2, 0, depth/2).rotateY(halfPi); topMat.identity().translate(0, -height/2, depth/2).rotateX(halfPi); bottomMat.identity().translate(0, height/2, depth/2).rotateX(-halfPi); writeTransforms(); } // set width, height, depth of cube setCubeSize(512, 288, 288); window.setCubeSize = setCubeSize; document.addEventListener('mousedown', dragStart, false); document.addEventListener('mousemove', dragMove, false); document.addEventListener('mouseup', dragEnd, false); } document.addEventListener('DOMContentLoaded', makeCubeGo, false); </script> <script> /** * this script handles creating an ascii version of the video and outputting it. * it's written by tab atkins then hacked up by paul irish */ var lw, lh, cw, ch, back, backcontext, out0, out1, out2, out3, cube, ascii = '@GLftli;:,. ', l = document.querySelector('#letter'), v = document.querySelector('#back'), draw = function(v, bc, w, h) { // as of pp7, ie9 throws a security exception for imageData of drawn video element try { bc.drawImage(v, 0, 0, w, h); var imgData = bc.getImageData(0, 0, w, h).data; draw = drawAscii; } catch(e) { draw = drawBackup; } draw(v, bc, w, h); }; function domReady(){ lw = l.offsetWidth; lh = l.offsetHeight; cw = Math.floor(v.offsetWidth /lw); ch = Math.floor(v.offsetHeight/lh); back = document.querySelector("#canvs"); backcontext = back.getContext('2d'); // output to the cube faces out0 = document.querySelector("#left"); out1 = document.querySelector("#top"); out2 = document.querySelector("#right"); out3 = document.querySelector("#bottom"); cube = document.querySelector('#cube'); } function drawAscii(v, bc, w, h) { if(v.paused || v.ended) return false; // First, draw the into the backing canvas bc.drawImage(v, 0, 0, w, h); // Grab the pixel data from the backing canvas var data = bc.getImageData(0, 0, w, h).data; var chars = '', px = 0, pxlen = w*h*4; // Loop through the pixels for(var ih = 0; ih < h; ih++) { for(var iw = 0; iw < w; iw++) { // Convert the color into an appropriate character based on luminance // magic numbers depend on ascii string length of 13, so scale accordingly chars += ascii[(62*data[px++]+123*data[px++]+23*data[px++])>>>12]; px++; // don't need alpha } chars += '\n'; } // Write the char data into the output divs out0.textContent = chars; out1.textContent = chars; out2.textContent = chars; out3.textContent = chars; // Start over! setTimeout(function(){ draw(v,bc,w,h); }, 0); } function drawBackup() { out0.style.backgroundColor = '#ccc'; out1.style.backgroundColor = '#ccc'; out2.style.backgroundColor = '#ccc'; out3.style.backgroundColor = '#ccc'; } addEventListener('DOMContentLoaded',domReady,false); v.addEventListener("play", function(e){ cw = Math.floor(v.offsetWidth/lw); ch = Math.floor(v.offsetHeight/lh); back.width = cw; back.height = ch; draw(this, backcontext, cw, ch); },false); document.querySelector('#res').addEventListener('change', function(e){ v.pause(); document.body.style.fontSize = e.target.value + 'px'; domReady(); // pause added to let the DOM catch up. setTimeout(function(){ v.play(); }, 10); }, false); function setPerspective(t) { var persp = 50000 * Math.exp(-5.20387 * t); cube.style[testr.perspectiveProp] = persp; } // perspective slider var perspSlider = document.querySelector('#persp'); if (testr.transform3d) { perspSlider.addEventListener('change', function(e){ setPerspective(e.target.value / 100); }, false); // set initial value setPerspective(perspSlider.value); } else { perspSlider.className += ' no3d'; } document.querySelector('input[type="checkbox"]').addEventListener('change', function(e) { document.body.className = '' + (e.target.checked ? 'fadey' : ''); }, false); document.body.addEventListener('mouseup', function f() { this.removeEventListener('mouseup', f, false); document.body.className = document.body.className.replace('unplayed', ''); }, false); </script> </body> </html>

Related: See More


Questions / Comments: