brand logo

Home | Games | News | Twitter | Youtube

converting-images-to-nes-chr-format.md

2026-05-11 by h2k1 #nesdev #graphics

NES PPU stores each tile in a 8x8 bit planar format. Each pixel has 2 bit to describe its palette index. So each tile weights 16 bytes, and each bit plane is 64 bits or 8 bytes.

It is easy to convert a grayscale PNG file to NES CHR format. For each 8x8 pixels tile, read each pixel, get its red (or green/blue) value, divide by 64 to get the value in range 0-3. Write each bit in seperate planes.

import { Jimp } from "jimp";
import { intToRGBA } from "@jimp/utils";

const picture = "picture.png"

let output = ""
let image = await Jimp.read(picture);
const ww = 32

for (let yy = 0; yy < 16; yy++) {
  for (let xx = 0; xx < ww; xx++) {
    let plane0 = "; plane0\n";
    let plane1 = "; plane1\n";
    for (let y = 0; y < 8; y++) {
      plane0 += ".byte %";
      plane1 += ".byte %";
      for (let x = 0; x < 8; x++) {
        const color = Math.floor(
          intToRGBA(image.getPixelColor(x + xx * 8, y + yy * 8)).r / 64,
        );
        plane0 += color == 1 || color == 3 ? "1" : "0";
        plane1 += color == 2 || color == 3 ? "1" : "0";
      }
      plane0 += "\n";
      plane1 += "\n";
    }
    output += plane0;
    output += plane1;
  }
}