CivArchive
    ← All articles
    Published September 27, 2025by n_Arno

    Pixelate V1.0 release!

    204 views2 reactions1 comments on CivitAI7 collected
    announcement

    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!