<div id="box">Drop an image in this window</div>
body, html {
  height: 100%;
}

body {
  -webkit-align-items: center;
  align-items: center;
  background: #222;
  color: #fff;
  display: -webkit-flex;
  display: flex;
  -webkit-justify-content: center;
  justify-content: center;
  margin: 0;
  transition: background-color 200ms;
}

#box {
  background: rgba(255, 255, 255, .06);
  border-radius: 20px;
  font-family: sans-serif;
  font-size: 4vh;
  max-height: 80%;
  overflow: auto;
  padding: 30px;
  text-shadow: 0 1px 1px rgba(0, 0, 0, .5);
  text-transform: uppercase;
}

#box > div {
  -webkit-align-items: center;
  align-items: center;
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: row;
  flex-direction: row;
  font-family: monospace;
  font-size: 14px;
  margin-bottom: 8px;
  text-transform: none;
}

#box > p {
  margin: 0 0 .5em;
}

#box > p button {
  background: rgba(0, 0, 0, .2);
  border: 0;
  border-radius: 4px;
  color: #fff;
  font-size: 14px;
  margin-right: 6px;
  padding: 6px 10px;
}

#box > p button:hover {
  background: rgba(0, 0, 0, .5);
}

#box > p button.selected {
  color: #0f0;
  font-weight: bold;
}

.color {
  border: #fff solid 1px;
  border-radius: 9999px;
  box-shadow: 0 2px 2px rgba(0, 0, 0, .5);
}

.drop-it-like-its-hot {
  background: #0a0;
}

.working-on-it {
  background: #888;
}

.oh-crap {
  background: #a00;
}
const body = document.body;
const box = document.getElementById('box');

function bytes(hex) {
  const bytes = [];
  for (let i = 0; i < hex.length; i += 2) {
    bytes.push(parseInt(hex.substr(i, 2), 16));
  }
  return bytes;
}

function hex(...bytes) {
  return bytes.reduce((hex, byte) => hex + (byte < 16 ? '0' : '') + byte.toString(16), '');
}

const adapters = {
  android: (r, g, b) => {
    return `Color.rgb(${r}, ${g}, ${b})`
  },
  hex: (r, g, b) => `#${hex(r, g, b)}`,
  swift: (r, g, b) => {
    [r, g, b] = [r, g, b].map(v => (v / 255).toPrecision(3));
    return `UIColor(red: ${r}, green: ${g}, blue: ${b}, alpha: 1)`
  }
};

class App {
  displayError(error) {
    box.textContent = error;
    body.classList.remove('working-on-it');
    body.classList.add('oh-crap');
    setTimeout(() => {
      body.classList.remove('oh-crap');
      if (box.textContent == error) {
        box.textContent = 'Drop an image in this window';
      }
    }, 2000);
  }

  dragenter() {
    body.classList.add('drop-it-like-its-hot');
  }
  
  dragleave() {
    body.classList.remove('drop-it-like-its-hot');
  }
  
  dragover(event) {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'copy';
    body.classList.add('drop-it-like-its-hot');
  }

  drop(event) {
    event.preventDefault();
    this.dragleave();
    const firstImage = Array.prototype.filter.call(event.dataTransfer.files, file => file.type.match(/^image\//))[0];
    if (!firstImage) { 
      this.displayError('Could not load image');
      return;
    }
    box.textContent = 'Please wait...';
    body.classList.add('working-on-it');
    setTimeout(() => {
      this.getCanvasFromFile(firstImage)
        .then(canvas => this.extractColors(canvas))
        .then(colors => {
          this.renderPalette(colors);
        })
        .then(() => body.classList.remove('working-on-it'))
        .catch(() => {
          this.displayError('Could not load image');
        });
    }, 500);
  }
  
  extractColors(canvas) {
    const palette = Object.create(null);
    const context = canvas.getContext('2d');
    const data = context.getImageData(0, 0, canvas.width, canvas.height).data;
    for (let i = 0; i < data.length; i += 4) {
      let color = hex(data[i], data[i + 1], data[i + 2]);
      palette[color] = (palette[color] || 0) + 1;
    }
    const numPixels = canvas.width * canvas.height;
    return Object.keys(palette)
      .map(color => ({color, count: palette[color], fraction: palette[color] / numPixels}))
      .sort((a, b) => b.count - a.count)
      .slice(0, 100);
  }
  
  getCanvasFromFile(file) {
    return new Promise((resolve, reject) => {
      const image = new Image();
      image.onerror = reject;
      image.onload = () => {
        const canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;
        const context = canvas.getContext('2d');
        context.drawImage(image, 0, 0);
        resolve(canvas);
      };
      image.src = URL.createObjectURL(file);
    });
  }
  
  handleEvent(event) {
    return this[event.type](event);
  }
  
  renderPalette(colors, adapter = null) {
    const fragment = document.createDocumentFragment();
    const p = document.createElement('p');
    for (let option in adapters) {
      if (!adapter) adapter = option;
      let button = document.createElement('button');
      button.textContent = option;
      button.onclick = () => this.renderPalette(colors, option);
      if (adapter == option) {
        button.className = 'selected';
      }
      p.appendChild(button);
    }
    fragment.appendChild(p);
    colors.forEach(({color, fraction}) => {
      const size = Math.ceil(Math.min(.3 + fraction * 20, 1) * 32);
      const div = document.createElement('div');
      div.innerHTML = `<div class="color" style="background: #${color};width:${size}px;height:${size}px;margin:0 ${32-size/2}px;" title="${(fraction * 100).toPrecision(3)}%"></div> ${adapters[adapter](...bytes(color))}`;
      fragment.appendChild(div)
    });
    box.innerHTML = '';
    box.appendChild(fragment);
  }
}

const app = new App();
['dragover', 'dragenter', 'dragleave', 'drop'].forEach(t => body.addEventListener(t, app));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.