Sign In

Pixelate V1.0 release!

7

Sep 27, 2025

(Updated: 4 months ago)

announcement
Pixelate V1.0 release!

A VERY simple tool to pixelate any picture. There is plenty of those, but here, everything run in your browser, it's written in Python and you can modify it easily without needing to be a ... burp ... frontend dev. 🤣😉

The civitai page is here is you want to add a like (it would be appreciated) and read about it, but you can also simply jump and use it here: https://huggingface.co/spaces/n-Arno/pixelate

The real "engine" is as "simple" as that:

    import numpy as np
    from PIL import Image
    from js import canvas, scale, colors, quant, rgb555, snescrop, rescale, addToOutput, ImageData
    from pyodide.ffi import create_proxy

    # Access to canvas content
    canvasCtx = canvas.getContext("2d")

    # SNES helpers
    toRGB555 = lambda v: v >> 3 << 3
    def apply(arr, fn):
        vfn = np.vectorize(fn,otypes=[arr.dtype])
        return vfn(arr.flatten()).reshape(arr.shape)
    def snes_crop(image):
        width, height = image.size
        new_width, new_height = 256, 224
        left = max([0, (width - new_width)/2])
        top = max([0, (height - new_height)/2])
        right = min([width, (width + new_width)/2])
        bottom = min([height, (height + new_height)/2])
        return image.crop((left, top, right, bottom))

    addToOutput("Starting...")

    # Read canvas ImageData as PIL.Image
    im = Image.fromarray(np.array(canvasCtx.getImageData(0,0,canvas.width,canvas.height,{"pixelFormat":"rgba-unorm8"}).data.to_py()).reshape(canvas.height,canvas.width,4),mode='RGBA')
    w, h = im.width, im.height
    addToOutput(f"Got image of size ({w},{h})")

    # Stupid pixel by resize
    s = int(scale.value)
    w, h = int(w/s), int(h/s)
    im = im.resize((w,h))
    addToOutput(f"Resized by scale {s} to ({w},{h})")

    # Reduce or quantize colors
    if quant.checked:
        im = im.quantize(colors=int(colors.value),method=Image.Quantize.LIBIMAGEQUANT).convert('RGBA')
        addToOutput(f"Quantized to {colors.value} colors")
    else:
        im = im.convert('RGB').quantize(colors=int(colors.value),method=Image.Quantize.MEDIANCUT).convert('RGBA')
        addToOutput(f"Reduced to {colors.value} colors")

    # Apply RGB555
    if rgb555.checked:
      im = Image.fromarray(apply(np.asarray(im.convert('RGB'),dtype='uint8'),toRGB555),'RGB').convert('RGBA')
      addToOutput(f"Applied RGB555")

    # Apply SNES crop
    if snescrop.checked:
      im = snes_crop(im)
      w, h = im.width, im.height
      addToOutput(f"Cropped to ({w},{h})")

    # Rescale post-processing
    if rescale.checked:
      im = im.resize((w*s,h*s))
      w, h = im.width, im.height
      addToOutput(f"Rescaled to ({w},{h})")

    # Convert back to ImageData
    im = np.asarray(im,dtype='uint8').tobytes()
    pixels_proxy = create_proxy(im)
    pixels_buf = pixels_proxy.getBuffer("u8clamped")
    img_data = ImageData.new(pixels_buf.data, w, h)
    canvas.width = w
    canvas.height = h
    canvasCtx.putImageData(img_data, 0, 0)
    pixels_proxy.destroy()
    pixels_buf.release()

    addToOutput("Done!")

The three main problems has been:

  • Compiling pyodide from source

  • Adding libimagequant and building pillow with it

  • Reading and writing to HTML canvas from the python code.

That's all folks!

7