// canvas settings var viewWidth = 768, viewHeight = 768, drawingCanvas = document.getElementById("drawing_canvas"), ctx; var images = [], currentImageIndex = 0; var threshold = 96; var points = [], pointGrid = []; var drawingLevel, drawingComplete = false; function initDrawingCanvas() { drawingCanvas.width = viewWidth; drawingCanvas.height = viewHeight; ctx = drawingCanvas.getContext('2d'); } function processImage(image) { var sampleScale = 0.25, infSampleScale = 1 / sampleScale, sampleWidth = viewWidth * sampleScale; ctx.drawImage(image, 0, 0, sampleWidth, sampleWidth); var imageData = ctx.getImageData(0, 0, sampleWidth, sampleWidth), pixels = imageData.data; for (var i = 0; i < pixels.length; i += 4) { var point, r = pixels[i + 0], g = pixels[i + 1], b = pixels[i + 2], avg = (r + g + b) / 3, x = ((i / 4) % sampleWidth) * infSampleScale, y = Math.floor((i / 4) / sampleWidth) * infSampleScale; if (avg > threshold) { point = new Point(x, y); point.numNeigbors = Math.floor(avg / 16); points.push(point); if (!pointGrid[x]) pointGrid[x] = []; pointGrid[x][y] = point; } } points.forEach(function(p) { p.neighbors = getClosestPoints(p); }); ctx.fillStyle = '#000' ctx.fillRect(0, 0, viewWidth, viewHeight); } function getClosestPoints(p) { var da = new Point(), db = new Point(), la = 0, lb = 0, proximityOffset = 4; var tp = getPointsInRadius(p, 24); tp.sort(function(a, b) { la = da.copy(a).sub(p).length(); lb = db.copy(b).sub(p).length(); if (la < lb) return -1; else if (la > lb) return 1; else return 0; }); return tp.slice(proximityOffset, proximityOffset + p.numNeigbors); } function getPointsInRadius(point, radius) { var n = []; for (var i = -radius + point.x; i < radius + point.x; i++) { if (pointGrid[i]) { for (var j = -radius + point.y; j < radius + point.y; j++) { if (pointGrid[i][j]) { n.push(pointGrid[i][j]); } } } } return n; } function update() { if (!drawingLevel) { drawingLevel = [points[0]]; } else { var currentLevel = drawingLevel.slice(0); drawingLevel.length = 0; for (var i = 0; i < currentLevel.length; i++) { currentLevel[i].neighbors.forEach(function(p) { if (!p.visited) { p.visited = true; drawingLevel.push(p); } }); } drawingComplete = drawingLevel.length === 0; } } function draw() { ctx.lineWidth = 0.1; ctx.strokeStyle = '#fff'; drawingLevel.forEach(function (p0) { p0.neighbors.forEach(function(p1) { ctx.beginPath(); ctx.moveTo(p0.x + Math.random() * 2 - 1, p0.y + Math.random() * 2 - 1); ctx.lineTo(p1.x + Math.random() * 2 - 1, p1.y + Math.random() * 2 - 1); ctx.stroke(); }); }); } window.onload = function() { loadImages(); }; // cross origin angry image loader function loadImages() { var urls = [ "https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/owl.jpg", "https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/tiger.jpg", "https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/lemur.jpg", "https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/rhino.jpg", "https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/lion.jpg" ], loaded = 0; urls.forEach(function(url) { var image = new Image(); image.crossOrigin = 'Anonymous'; image.onload = function () { images[urls.indexOf(url)] = image; if (++loaded === urls.length) { imagesLoaded(); } }; image.src = url; }); } function imagesLoaded() { initDrawingCanvas(); processImage(images[currentImageIndex]); document.getElementById('next_button').onclick = nextButtonClickHandler; requestAnimationFrame(loop); } function nextButtonClickHandler() { if (drawingComplete === false) return; if (++currentImageIndex === images.length) { currentImageIndex = 0; } points = []; pointGrid = []; drawingLevel = null; drawingComplete = false; processImage(images[currentImageIndex]); requestAnimationFrame(loop); } function loop() { update(); draw(); if (!drawingComplete) { requestAnimationFrame(loop); } } Point = function(x, y) { this.set(x, y); }; Point.prototype = { add:function(p) { this.x += p.x; this.y += p.y; return this; }, sub:function(p) { this.x -= p.x; this.y -= p.y; return this; }, mul:function(s) { this.x *= s; this.y *= s; return this; }, div:function(s) { this.x /= s; this.y /= s; return this; }, set:function(x, y) { this.x = x || 0; this.y = y || 0; return this; }, copy:function(p) { this.x = p.x; this.y = p.y; return this; }, length:function() { return Math.sqrt(this.x * this.x + this.y * this.y); } };