~120 minutes. Modify one 8x8 sprite tile by changing its raw bytes in CHR-ROM. Use what you learned in lab 2.2 about the 2-bitplane encoding.
Goal: redesign one specific 8x8 tile (the player character's head, an enemy face, a fruit pickup, anything visible) by editing the 16 bytes that encode it.
Estimated time: 120 minutes (the most ambitious lab of week 4)
Prerequisites: lab 2.2 complete (you understand the 2-bitplane tile encoding); lab 4.1 and 4.2 complete
Steps
Step 1: Pick a tile (15 minutes)
Run your ROM in Mesen. Find a clearly identifiable tile you want to redesign. Strong candidates:
- The player character's face / head
- An enemy face
- An item / pickup sprite (coin, key, fruit)
- A letter from the title screen
- A particular environment tile (a brick; a tree)
Open Mesen's PPU Viewer → Pattern Tables. Find the tile and note its tile index. Confirm by hovering, Mesen should highlight the corresponding location.
Write down: "I want to change tile index ___ in pattern table ___. It currently looks like ___. I will redraw it as ___."
Step 2: Plan your redesign on graph paper (or digitally) (20 minutes)
Sketch the new 8×8 design. You have 4 color slots (palette indices 0, 1, 2, 3), that is, each pixel can be one of 4 values. Plan which pixels are each color.
A typical 8x8 sketch on graph paper:
. . X X X X . .
. X X X X X X .
X X 1 1 1 1 X X
X X 1 2 2 1 X X
X X 1 2 2 1 X X
X X 1 1 1 1 X X
. X X X X X X .
. . X X X X . .
(Where . = palette index 0 (transparent/background), X = palette index 3 (outline), 1 = palette index 1 (fill), 2 = palette index 2 (detail).)
Step 3: Convert your sketch to bytes (30 minutes)
You have an 8×8 grid where each cell is one of {0, 1, 2, 3}. You need to convert this to 16 bytes (8 low-bitplane bytes + 8 high-bitplane bytes).
For each row r (0 to 7):
- low_byte[r] = the 8 LOW bits of the pixels in row r, packed MSB-first
- high_byte[r] = the 8 HIGH bits of the pixels in row r, packed MSB-first
For pixel value v, the low bit is v & 1 and the high bit is (v >> 1) & 1. (So palette 0 → low=0, high=0; palette 1 → low=1, high=0; palette 2 → low=0, high=1; palette 3 → low=1, high=1.)
Example for the row . X 1 2 2 1 X .:
Palette indices: 0 3 1 2 2 1 3 0
Low bits: 0 1 1 0 0 1 1 0 → binary 01100110 → 0x66
High bits: 0 1 0 1 1 0 1 0 → binary 01011010 → 0x5A
Do this for all 8 rows. Write down all 16 bytes.
Step 4: Find the tile's bytes in your ROM (15 minutes)
Use the offset math from lab 2.2:
- CHR-ROM start = 16 (header) + PRG-ROM size in bytes
- Tile offset within CHR-ROM = tile index × 16 + (4096 if pattern table 1)
- File offset = CHR-ROM start + tile offset
Open your hex editor at that file offset. The 16 bytes you see are the original tile encoding.
Step 5: Write the new bytes (10 minutes)
Overwrite the 16 bytes at the tile's file offset with your new 16 bytes. The first 8 bytes are the low bitplane; the next 8 are the high bitplane.
Save the file.
Step 6: Verify in Mesen (15 minutes)
Reload your hack in Mesen. Open Tools → PPU Viewer → Pattern Tables. Find your tile (same index). Does it match your sketch?
- Yes, exactly: great. Now run the game and find where the tile appears. Confirm it shows up in context (the player's face IS now your new design; the enemy IS now your modified version)
- The tile is there but the colors are weird: the palette assigned to this tile in the game is different from what you assumed. Your shape is correct; the colors are determined elsewhere
- The tile is garbled: your byte conversion was wrong. Re-check step 3. Most likely: you swapped low and high bitplane bytes, or you reversed the column ordering (column 0 is bit 7, not bit 0)
Step 7: Take screenshots (10 minutes)
Save:
~/spk-101/working/lab-4-3-before-tileview.png(PPU viewer of the original tile)~/spk-101/working/lab-4-3-after-tileview.png(PPU viewer of your modified tile)~/spk-101/working/lab-4-3-before-ingame.png(the game showing the original tile in context)~/spk-101/working/lab-4-3-after-ingame.png(the game showing your modified tile in context)
Step 8: Journal (15 minutes)
In ~/spk-101/journal/lab-4-3-notes.md:
- The tile you picked and your sketch
- Your row-by-row byte conversion (at least the first two rows in full detail)
- Where the tile lived in the ROM (file offset)
- What you tried that did not work first
- A reflection: this lab packed an 8×8 image (64 pixels, 4 colors per pixel) into 16 bytes. A 1989 PNG with the same data would be ~80 bytes (header + chunks + compressed pixel data); a 2025 PNG could be much smaller because of better compression. What is the tradeoff the NES designers made by choosing this very-compact custom encoding?
Expected output
- One redesigned 8x8 sprite tile, visible in your hacked ROM
- 4 screenshots (2 tile-viewer, 2 in-game)
- A detailed journal entry showing your byte-conversion work
Common pitfalls
- Confusing low and high bitplane bytes: the FIRST 8 bytes of a tile are the LOW bitplane; the NEXT 8 are the HIGH bitplane. Swapping them shows the right shape with the wrong colors
- Reversing column order: column 0 is the LEFTMOST pixel and corresponds to BIT 7 (MSB) of each row byte. Bit 0 is the rightmost
- Off-by-one tile index: tile index 0 starts at offset CHR-ROM-start; tile index 1 starts at CHR-ROM-start + 16. Confirm before editing
- Editing a tile that does not appear during normal gameplay: some tiles are used only in specific contexts. You may have edited a tile that never shows during the part of the game you tested. Check Mesen's PPU Viewer to confirm your edit took effect; then play more of the game to find it
Stretch (optional)
- Redesign a second tile that combines with the first (e.g., the player's face AND the player's body, so the full character is yours)
- Use a free pixel-art editor (Aseprite trial; GIMP; even MS Paint at 8x zoom) to design your tile, then export the bytes by hand. Some PPU viewers can import pixel-art directly
- Find one tile that is repeated in multiple places (e.g., the same brick used 50 times in a level). Modifying it changes ALL those places at once. Use this for stylistic effect
Lab 4.3 v0.1. The most ambitious week-4 lab. Genuine pixel art on real hardware emulation.