This guide covers everything needed to create graphics for SNES games using OpenSNES.
Table of Contents
- PPU Architecture
- Video Modes
- Backgrounds
- Sprites
- Metasprites
- VRAM Layout
- Palettes
- Asset Requirements
- Tools & Resources
PPU Architecture
The SNES uses two PPU chips working together:
| Chip | Name | Function |
| PPU1 | 5C77 | "The Brain" - addresses VRAM, makes rendering decisions |
| PPU2 | 5C78 | "The Compositor" - generates video signal, accesses palettes |
Memory Systems
| Memory | Size | Purpose |
| VRAM | 64 KB | Tiles, tilemaps for backgrounds and sprites |
| OAM | 544 bytes | Sprite attributes (128 sprites) |
| CGRAM | 512 bytes | Color palettes (256 colors × 15-bit) |
Video Modes
The SNES supports 8 background modes. Mode 1 is most common for games:
| Mode | BG1 | BG2 | BG3 | BG4 | Notes |
| 0 | 2bpp | 2bpp | 2bpp | 2bpp | 4 layers, 4 colors each |
| 1 | 4bpp | 4bpp | 2bpp | - | Most common - 2×16 colors + 1×4 colors |
| 2 | 4bpp | 4bpp | - | - | Per-tile offset (parallax) |
| 3 | 8bpp | 4bpp | - | - | 256 colors + 16 colors |
| 4 | 8bpp | 2bpp | - | - | Per-tile offset |
| 5 | 4bpp | 2bpp | - | - | Hi-res (512px wide) |
| 6 | 4bpp | - | - | - | Hi-res + per-tile offset |
| 7 | 8bpp | - | - | - | Rotation/scaling (Mode 7) |
Mode 1 Setup (Recommended)
#define REG_BGMODE
BG mode and tile size (W).
Definition registers.h:64
Backgrounds
Tilemap Structure
Each tilemap entry is 16 bits:
VHOP PPCC CCCC CCCC
│││ ││└─────────── Tile number (0-1023)
│││ └└───────────── Palette (0-7)
││└───────────────── Priority (0-1)
│└────────────────── Horizontal flip
└─────────────────── Vertical flip
Tilemap Sizes
| Setting | Tiles | Pixels | Memory |
| 32×32 | 1024 | 256×256 | 2 KB |
| 64×32 | 2048 | 512×256 | 4 KB |
| 32×64 | 2048 | 256×512 | 4 KB |
| 64×64 | 4096 | 512×512 | 8 KB |
Tile Formats
| Bit Depth | Colors | Bytes/Tile | Use Case |
| 2bpp | 4 | 16 bytes | HUD, simple BG |
| 4bpp | 16 | 32 bytes | Main backgrounds |
| 8bpp | 256 | 64 bytes | Detailed art |
Sprites
OAM Structure (544 bytes total)
Main Table (512 bytes, 4 bytes × 128 sprites):
| Byte | Bits | Content |
| 0 | 7-0 | X position (low 8 bits) |
| 1 | 7-0 | Y position |
| 2 | 7-0 | Tile number (low 8 bits) |
| 3 | 7 | Vertical flip |
| 3 | 6 | Horizontal flip |
| 3 | 5-4 | Priority (0-3) |
| 3 | 3-1 | Palette (0-7, from CGRAM $80-$FF) |
| 3 | 0 | Tile number bit 8 |
Extended Table (32 bytes, 2 bits × 128 sprites):
- Bit 0: X position bit 8 (for X > 255)
- Bit 1: Size select (0=small, 1=large)
Sprite Sizes
Register $2101 selects two sizes (small/large):
| OBJSEL | Small | Large |
| 0 | 8×8 | 16×16 |
| 1 | 8×8 | 32×32 |
| 2 | 8×8 | 64×64 |
| 3 | 16×16 | 32×32 |
| 4 | 16×16 | 64×64 |
| 5 | 32×32 | 64×64 |
| 6 | 16×32 | 32×64 |
| 7 | 16×32 | 32×32 |
Scanline Limits (Critical!)
| Limit | Value | Consequence |
| Sprites/line | 32 max | Higher OAM index sprites dropped |
| Slivers/line | 34 max | Sprite parts clipped |
Solution: Rotate OAM priority each frame to distribute flicker.
Sprite Priority
Within OBJ layer: Lower OAM index = on top With backgrounds: Priority bits 0-3 interleave with BG layers
Metasprites
Metasprites combine multiple hardware sprites into one logical entity.
Data Format Example
const u8 player_idle[] = {
0, 0, 0x00, 0x30,
8, 0, 0x01, 0x30,
0, 8, 0x10, 0x30,
8, 8, 0x11, 0x30,
0xFF
};
unsigned char u8
8-bit unsigned integer (0 to 255)
Definition types.h:47
Animation State Machine
typedef struct {
} Animation;
void animation_update(Animation* anim) {
if (--anim->timer == 0) {
anim->frame++;
if (anim->frames[anim->frame] ==
NULL) {
anim->frame = anim->loop_start;
}
anim->timer = FRAME_DURATION;
}
}
#define NULL
Null pointer constant.
Definition types.h:167
u16 frame
Current sprite tile number.
Definition main.c:81
VRAM Layout
Recommended Layout (Mode 1)
| Address | Size | Content |
| $0000 | 24 KB | BG tiles (4bpp, 768 tiles) |
| $6000 | 2 KB | BG1 tilemap |
| $6800 | 2 KB | BG2 tilemap |
| $7000 | 2 KB | BG3 tilemap |
| $8000 | 16 KB | Sprite tiles (4bpp, 512 tiles) |
VRAM Address Registers
| Register | Purpose |
| $2107 | BG1 tilemap address (bits 7-2 × $400) |
| $2108 | BG2 tilemap address |
| $2109 | BG3 tilemap address |
| $210B | BG1/2 tile address (nibbles × $1000) |
| $210C | BG3/4 tile address |
| $2101 | Sprite tile address + size select |
Palettes
CGRAM Layout (256 colors × 2 bytes)
| Range | Colors | Use |
| $00-$7F | 128 | Background palettes (8 × 16 colors) |
| $80-$FF | 128 | Sprite palettes (8 × 16 colors) |
Color Format (15-bit BGR)
0BBB BBGG GGGR RRRR
│││││││││││└────── Red (0-31)
│││││└└└└└──────── Green (0-31)
└└└└└───────────── Blue (0-31)
Palette Selection
Backgrounds: Tilemap bits 10-12 select palette 0-7 Sprites: OAM byte 3 bits 1-3 select palette 0-7 (from upper CGRAM)
Asset Requirements
For SNES Compatibility
| Asset Type | Requirements |
| Tiles | 8×8 pixels, indexed color |
| BG Palette | Max 16 colors per palette (4bpp) |
| Sprite Palette | Max 16 colors, color 0 = transparent |
| Tilemap | Max 1024 unique tiles per layer |
| Character | 16×16 to 32×32 typical |
Recommended Workflow
- Create art in any tool (Aseprite, GIMP, Photoshop)
- Reduce colors to 16 per palette
- Export as indexed PNG (8×8 grid alignment)
- Convert with gfx4snes or similar tool
- Load via DMA during VBlank
Tools & Resources
Graphics Conversion Tools
| Tool | Purpose | Link |
| gfx4snes | PNG to SNES tiles/palettes | OpenSNES/tools |
| M1TE2 | Tile editor, map maker | Third-party |
| SuperTileMapper | VRAM/tilemap editor | GitHub |
| Aseprite | Pixel art (paid) | aseprite.org |
| GIMP | Free image editor | gimp.org |
Free Asset Sources (CC0/Public Domain)
Documentation
Quick Reference
Register Cheat Sheet
$2100 INIDISP - Screen on/off, brightness
$2101 OBJSEL - Sprite VRAM address, sizes
$2105 BGMODE - Background mode, tile size
$2107 BG1SC - BG1 tilemap address, size
$210B BG12NBA - BG1/2 tile address
$212C TM - Main screen layer enable
$212D TS - Sub screen layer enable
$2115 VMAIN - VRAM address increment mode
$2116 VMADDL - VRAM address (low)
$2117 VMADDH - VRAM address (high)
$2118 VMDATAL - VRAM data write (low)
$2119 VMDATAH - VRAM data write (high)
$2121 CGADD - Palette address
$2122 CGDATA - Palette data write
DMA Registers (for fast transfers)
$4300 DMAP0 - DMA parameters
$4301 BBAD0 - B-bus address ($18 for VRAM, $22 for CGRAM)
$4302 A1T0L - Source address (low)
$4303 A1T0H - Source address (high)
$4304 A1B0 - Source bank
$4305 DAS0L - Transfer size (low)
$4306 DAS0H - Transfer size (high)
$420B MDMAEN - Start DMA (bit 0 = channel 0)