Skip to content

Pixelate

A canvas-based image pixelation component. Converts any image into pixel-art style with configurable block size, grayscale mode, and custom color palette mapping.

Inspired by Pixelium Design and Pixel-UI Pixelit.

NOTE

The demo images have no particular meaning — they are just colorful samples to showcase the pixelation effect.

NameCategoryDescriptionTypeDefault
pixelSize? StylePixel block size (higher = more pixelated)number8
grayscale? StyleEnable grayscale modebooleanfalse
width? SizeComponent widthnumber | string
height? SizeComponent heightnumber | string
palette? ColorCustom color palette (hex strings or RGB tuples)string[] | number[][]
background? ColorBackground color for transparent areasstring'#FFFFFF'
src? ContentImage source URL or base64string
rendered? EventEmitted after pixelation rendering completes() => void
error? EventEmitted when the image fails to load(event: Event) => void
canvasRef? ExposeCanvas element refHTMLCanvasElement
originRef? ExposeHidden original image element refHTMLImageElement
render()? MethodManually trigger re-render() => void
getSize()? MethodGet rendered canvas dimensions() => { width: number; height: number }
getImageData()? MethodGet pixelated ImageData() => ImageData | null

Basic Usage

Provide an image src to pixelate it with the default pixel size of 8.

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
    <px-pixelate src="/images/pixelate/001.png" :width="400" />
  </div>
</template>

Pixel Size

Use pixel-size to control the level of pixelation. Higher values produce larger pixel blocks.

<template>
  <div>
    <div style="margin-bottom: 12px">
      <label>Pixel Size: {{ pixelSize }}</label>
      <input type="range" v-model.number="pixelSize" min="1" max="32" style="margin-left: 8px" />
    </div>
    <px-pixelate src="/images/pixelate/001.png" :pixel-size="pixelSize" :width="400" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const pixelSize = ref(8);
</script>

Grayscale

Enable grayscale to convert the pixelated output to grayscale using the ITU-R BT.601 luma formula.

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
    <div>
      <p style="margin-bottom: 8px">Original</p>
      <px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="300" />
    </div>
    <div>
      <p style="margin-bottom: 8px">Grayscale</p>
      <px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" grayscale :width="300" />
    </div>
  </div>
</template>

Custom Palette

Use palette to map pixel colors to a limited set. Supports hex strings (e.g. '#FF0000') or RGB tuples (e.g. [255, 0, 0]). Each pixel block is mapped to the nearest palette color using Euclidean distance in RGB space.

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
    <div>
      <p style="margin-bottom: 8px">Original</p>
      <px-pixelate src="/images/pixelate/001.png" :pixel-size="4" :width="260" />
    </div>
    <div>
      <p style="margin-bottom: 8px">Game Boy Palette</p>
      <px-pixelate src="/images/pixelate/001.png" :pixel-size="4" :palette="gameboyPalette" :width="260" />
    </div>
    <div>
      <p style="margin-bottom: 8px">Sunset Palette</p>
      <px-pixelate src="/images/pixelate/001.png" :pixel-size="4" :palette="sunsetPalette" :width="260" />
    </div>
  </div>
</template>

<script setup lang="ts">
// Classic Game Boy green palette
const gameboyPalette = ['#0f380f', '#306230', '#8bac0f', '#9bbc0f'];

// Warm sunset palette
const sunsetPalette = ['#2b0f3e', '#5c2d82', '#c0392b', '#e67e22', '#f1c40f', '#ecf0f1'];
</script>

Dimensions

Use width and height to control the rendered size. Accepts numbers (pixels) or strings (CSS units like '50%').

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end">
    <div>
      <p style="margin-bottom: 8px">100 × 100</p>
      <px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="100" :height="100" />
    </div>
    <div>
      <p style="margin-bottom: 8px">200 × 150</p>
      <px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="200" :height="150" />
    </div>
    <div>
      <p style="margin-bottom: 8px">400 (auto height)</p>
      <px-pixelate src="/images/pixelate/002.jpg" :pixel-size="4" :width="400" />
    </div>
  </div>
</template>

Exposed Methods

Access the component instance via a template ref to call render(), getSize(), and getImageData().

<template>
  <div>
    <div style="margin-bottom: 12px; display: flex; gap: 8px; flex-wrap: wrap">
      <px-button type="primary" @click="reRender">Re-render</px-button>
      <px-button @click="showSize">Get Size</px-button>
      <px-button @click="showImageData">Get ImageData</px-button>
    </div>
    <p v-if="info" style="margin-bottom: 8px; font-family: monospace">{{ info }}</p>
    <px-pixelate ref="pixelateRef" src="/images/pixelate/001.png" :pixel-size="pixelSize" :width="400" />
  </div>
</template>

<script setup lang="ts">
import type { PixelateInstance } from 'sakana-element';
import { ref } from 'vue';

const pixelateRef = ref<PixelateInstance>();
const info = ref('');
const pixelSize = ref(8);

function reRender() {
  pixelSize.value = Math.max(1, Math.floor(Math.random() * 16) + 1);
  info.value = `Re-rendered with pixelSize = ${pixelSize.value}`;
}

function showSize() {
  const size = pixelateRef.value?.getSize();
  info.value = size ? `Canvas size: ${size.width} × ${size.height}` : 'Not rendered yet';
}

function showImageData() {
  const data = pixelateRef.value?.getImageData();
  info.value = data
    ? `ImageData: ${data.width} × ${data.height} (${data.data.length} bytes)`
    : 'No ImageData available';
}
</script>