var QUALITY_X = 6, QUALITY_Y = 3, WIDTH = Math.floor(window.innerWidth / QUALITY_X), HEIGHT = Math.floor(window.innerHeight / QUALITY_Y), SIZE = WIDTH * HEIGHT, HEIGHT_HALF = Math.floor(HEIGHT / 2), TEXT_OFFSETY = Math.floor((HEIGHT - 64) / 2), context, image, data, buffer1, buffer2, tempbuffer, canvasHeightMap, contextHeightMap, imageHeightMap, dataHeightMap, canvasText, contextText, imageText, dataText, input, text, isUserInteracting, pointers; init(); setInterval(loop, 1000 / 60); function init() { var container, canvas; container = document.getElementById('container'); // input box input = document.createElement("input"); input.type = "text"; input.value = "type"; input.style.position = "absolute"; input.style.top = "10px"; input.style.left = "10px"; input.style.opacity = "0"; container.appendChild(input); input.focus(); // Height Map (Water) canvasHeightMap = document.createElement("canvas"); canvasHeightMap.width = WIDTH; canvasHeightMap.height = HEIGHT; contextHeightMap = canvasHeightMap.getContext("2d"); imageHeightMap = contextHeightMap.getImageData(0, 0, WIDTH, HEIGHT); dataHeightMap = imageHeightMap.data buffer1 = []; buffer2 = []; for (var i = 0; i < SIZE; i ++) { buffer1[i] = 128; buffer2[i] = 128; } // Text canvasText = document.createElement("canvas"); canvasText.width = WIDTH; canvasText.height = 128; contextText = canvasText.getContext("2d"); contextText.font = "80px Helvetica"; contextText.fillStyle = "rgb(255, 0, 0)"; contextText.textAlign = "center"; // Output (Parallax) canvas = document.createElement("canvas"); canvas.width = WIDTH; canvas.height = HEIGHT; canvas.style.width = window.innerWidth + "px"; canvas.style.height = window.innerHeight + "px"; container.appendChild(canvas); context = canvas.getContext ("2d"); context.fillStyle = "rgb(0, 0, 0)"; context.fillRect(0, 0, WIDTH, HEIGHT); image = context.getImageData(0, 0, WIDTH, HEIGHT); data = image.data; document.addEventListener('mousedown', onDocumentMouseDown, false); document.addEventListener('mousemove', onDocumentMouseMove, false); document.addEventListener('mouseup', onDocumentMouseUp, false); document.addEventListener('mouseout', onDocumentMouseOut, false); document.addEventListener('touchstart', onDocumentTouchStart, false); document.addEventListener('touchmove', onDocumentTouchMove, false); document.addEventListener('touchend', onDocumentTouchEnd, false); document.addEventListener('keypress', onDocumentKeyPress, false); } // Event Handlers function onDocumentMouseDown(event) { event.preventDefault(); isUserInteracting = true; input.focus(); pointers = [[event.clientX / QUALITY_X, event.clientY / QUALITY_Y]]; } function onDocumentMouseMove(event) { pointers = [[event.clientX / QUALITY_X, event.clientY / QUALITY_Y]]; } function onDocumentMouseUp(event) { isUserInteracting = false; } function onDocumentMouseOut(event) { isUserInteracting = false; } function onDocumentTouchStart(event) { isUserInteracting = true; event.preventDefault(); pointers = []; for (var i = 0; i < event.touches.length; i++) { pointers.push([event.touches[i].pageX / QUALITY_X, event.touches[i].pageY / QUALITY_Y]); } } function onDocumentTouchMove(event) { event.preventDefault(); pointers = []; for (var i = 0; i < event.touches.length; i++) { pointers.push([event.touches[i].pageX / QUALITY_X, event.touches[i].pageY / QUALITY_Y]); } } function onDocumentTouchEnd(event) { event.preventDefault(); isUserInteracting = false; } function onDocumentKeyPress(event) { switch(event.keyCode) { case 13: input.value = ""; break; } } // function emit(x, y) { buffer1[Math.floor(x) + (Math.floor(y + 40) * WIDTH)] = 256; } function writeText(string) { text = string; contextText.clearRect(0, 0, WIDTH, 128); contextText.fillText(string, WIDTH / 2, 63); imageText = contextText.getImageData(0, 0, WIDTH, 128); dataText = imageText.data; } function processText() { for(y = 0; y < 128; y++) { for(x = 0; x < WIDTH; x++) { if (dataText[(x + y * WIDTH) * 4] > 0) { emit(x, y + TEXT_OFFSETY); } } } } function loop() { var x, y, yz, pixel, index, indices; if (isUserInteracting) { for (var i = 0; i < pointers.length; i++) { emit(pointers[i][0], pointers[i][1]); } } // Water var pixel, iMax = (WIDTH * HEIGHT) - WIDTH; for (var i = WIDTH; i < iMax; i++) { pixel = ((buffer1[i - 1] + buffer1[i + 1] + buffer1[i - WIDTH] + buffer1[i + WIDTH]) >> 1) - buffer2[i]; buffer2[i] = pixel -= (pixel - 128) >> 4; dataHeightMap[i * 4] = pixel > 255 ? 255 : pixel < 0 ? 0 : pixel; } tempbuffer = buffer1; buffer1 = buffer2; buffer2 = tempbuffer; // Text if (text != input.value) { writeText(input.value); } processText(); // Parallax // Thx: http://pixelero.wordpress.com/2009/07/05/belousovzhabotinsky-with-perspective/ indices = []; for(x = 0; x < WIDTH; x++) { levels = []; pixels = []; for(y = 0; y < HEIGHT; y++) { index = indices[y] = (x + y * WIDTH) * 4; pixels[y] = dataHeightMap[index]; levels[y - (dataHeightMap[index] * HEIGHT >> 10) ] = y; } for(y = 0, yz = -1; y < HEIGHT; y++) { pixels[y] = levels[y] > yz ? pixel = pixels[yz = levels[y]] : pixel; index = indices[y]; data[index + 1] = pixel - 64 + (y >> 2); data[index + 2] = pixel - 32 + (y >> 2); } } context.putImageData(image, 0, 0); }