Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <canvas id="cellCanvas"></canvas>
<!-- Works with any image -->
<img id="imageToBeUsed" src="http://img2.timeinc.net/people/i/2013/pets/news/130304/kitten-3-600.jpg"/>
              
            
!

CSS

              
                /* For more check out zachsaucier.com */
              
            
!

JS

              
                var imgCanvas = document.createElement('canvas'),
    imgContext = imgCanvas.getContext('2d'),
    pixelInterval = 5, // Rather than inspect every single pixel in the image inspect every 5^2 pixel
    img = document.getElementById("imageToBeUsed"), // The image to be averaged
    cellSize = 20, // The size of square to average the color
    cw, // Canvas width
    ch; // Canvas height

var cellCanvas = document.getElementById("cellCanvas"),
    cellContext = cellCanvas.getContext('2d');

// Use a CORS proxy to allow the image to be used
img.crossOrigin = "Anonymous";
img.src = "https://crossorigin.me/" + img.src;
img.onload = function() {
  // Once the CORS enabled image loads, do our work
  newImg(img);
  animateColorAvg();
}

var data; // A 1D array containing the image color info

var numXCells,
    numYCells;

// Updates the image to be used
function newImg(imgElem) {
  img = imgElem;
  
  // Set the height and width of the canvas elements to that of the image
  cw = imgCanvas.width = cellCanvas.width = img.naturalWidth || img.offsetWidth || img.width;
  ch = imgCanvas.height = cellCanvas.height = img.naturalHeight || img.offsetHeight || img.height;
  
  // Force the area to be divisible by the cell size (prevents warping)
  while(cw * ch / cellSize % 1 != 0)
  	cellSize++;
  
  // Draw the image to our hidden canvas for calculations
  imgContext.drawImage(img, 0, 0);
  
  // Set the image data
  try {
    data = imgContext.getImageData(0, 0, cw, ch);
  } catch(e) {
    // Catch errors - usually due to cross domain security issues
    console.log(e);
    return;
  }

  data = data.data;
}

// Animate the movement of the cells
var offset = 0.1,
    playSpeed = 100, // Determines how often the canvas is updated
    lastTime = Date.now(),
    count = 0,
    direction = 1,
    // Offsets generated using https://jsfiddle.net/gyr5m5bL/
    offsets = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9];

function animateColorAvg() {
  // Used for debugging purposes
  //if(Date.now() - lastTime > playSpeed) {
		lastTime = Date.now();
    
    if(offsets[count])
    	offset += offsets[count];
    
    count += direction;
    
    if(count >= offsets.length)
      direction = -1;
    if(count <= 0) {
      direction = 1;
    }
    
    offset = Math.round(offset);
    
    gridifyAvgColors(offset % cellSize);
  //}

  window.requestAnimationFrame(animateColorAvg);
}

// Creates a grid of average colors of the current image
function gridifyAvgColors(cellOffset) {
  numXCells = Math.ceil(cw / cellSize);
  numYCells = Math.ceil(ch / cellSize);
  
  // Start at the bottom right, go to top left
  for(var i = numXCells; i >= 0; i--) {
    for(var j = numYCells; j >= 0; j--) {
      var x = i * cellSize - cellSize + cellOffset,
          y = j * cellSize - cellSize + cellOffset,
          color = getAvgColorAsRGB(x, y);
      
      cellContext.fillStyle = "rgb(" + color.r + ", " + color.g + ", " + color.b + ")";
      cellContext.fillRect(x, y, cellSize, cellSize);
    }
  }
}

// Gets the average color of the requested area
function getAvgColorAsRGB(startX, startY) {
	var rgb = { r:0, g:0, b:0 };
  
  // Check to see if a cell placed at this location covers any part of the image
  if(!(  (startX < cw
       && startY < ch)
      || (startX + cellSize >= 0
       && startY + cellSize >= 0)
  )) 
   return rgb; // Don't calculate anything because it's not covering a part of the image
  
  var cellSizeX = cellSize,
      cellSizeY = cellSize;
  
  // Account for negative starting values that still cover a part of the image
  if(startX < 0) {
    cellSizeX = 0 - startX;
    startX = 0;
  }
  if(startY < 0) {
    cellSizeY = 0 - startY;
    startY = 0;
  }
  
  // Account for values that go outside of the image
  if(startX + cellSizeX > cw && startX != cw)
    cellSizeX = cw - startX;
  if(startY + cellSizeY > ch && startY != ch)
    cellSizeY = ch - startY;
  
  var count = 0;
      
  for(var j = startY; j < startY + cellSizeY; j += pixelInterval) { 
  	for(var i = startX; i < startX + cellSizeX; i += pixelInterval) { // Do this second for lookup perf reasons
      count++;
      var index = (i + j * numXCells * cellSize) * 4;
      rgb.r += data[index];
      rgb.g += data[index + 1];
      rgb.b += data[index + 2];
    }
  }
  
  // Round the number values
  rgb.r = Math.floor(rgb.r / count);
  rgb.g = Math.floor(rgb.g / count);
  rgb.b = Math.floor(rgb.b / count);

  return rgb;
}
              
            
!
999px

Console