Image-to-Pixel

Examples

Load, dither, download

Open an image, pick a palette and a dither method, save a PNG. The smallest useful program.

Source
<div class="demo-area">
  <div class="controls">
    <input type="file" id="file" accept="image/*" aria-label="Choose an image">
    <select id="palette" aria-label="Palette"></select>
    <select id="method" aria-label="Dither method"></select>
    <button class="btn primary" id="save" type="button">Download PNG</button>
  </div>
  <canvas class="out" id="out" width="1" height="1"></canvas>
  <p class="note" id="info"></p>
</div>

<script type="module">
  import { createPixelator, PALETTES, DITHER_METHODS, downloadBlob } from '../../lib/image-to-pixel.bundle.esm.js';

  const $ = id => document.getElementById(id);
  for (const p of PALETTES) $('palette').add(new Option(`${p.name} (${p.colors.length})`, p.id, false, p.id === 'sweetie-16'));
  for (const m of DITHER_METHODS) $('method').add(new Option(m.label, m.id, false, m.id === 'bayer4'));

  const px = await createPixelator();          // WebGL 2 through ShaderMate, CPU if unavailable
  let image = new Image();
  image.src = '../../assets/samples/dusk.svg';
  await image.decode();
  let result;

  async function update() {
    result = await px.process(image, {
      size: { width: 120 },
      palette: { source: 'builtin', id: $('palette').value },
      dither: { method: $('method').value, strength: 0.8 },
    });
    result.toCanvas(1, { canvas: $('out') });     // CSS scales it up with image-rendering: pixelated
    $('info').textContent = `${result.width} × ${result.height}, ${result.usedColors().length} colours, ${result.ms.toFixed(1)} ms on ${result.backend}`;
  }

  $('file').onchange = async e => { image = await createImageBitmap(e.target.files[0]); update(); };
  $('palette').onchange = $('method').onchange = update;
  $('save').onclick = async () => downloadBlob(await result.toBlob({ scale: 4 }), 'pixel-art.png');
  update();
</script>