How the Super NES drew its worlds.
We start from the very beginning — what a digital picture even is — assuming you know nothing about computer graphics. Once the vocabulary is solid, we follow a frame through the Super Nintendo's two picture-processing chips and out to the electron beam. Sixteen modules — fourteen of them with live, procedurally-rendered labs — so you can drag, paint and break every idea, not just read it. No game artwork ships with this page; every pixel is drawn in your browser.
2D graphics from zero
Pixels, 15-bit colour, tiles and bitplanes, tilemaps, sprites, layers and the electron beam — the vocabulary of 2D hardware, explained from scratch.
The SNES PPU
The S-PPU1/S-PPU2 pair, the eight background modes, Mode 7's affine floor, 128 sprites, colour math and HDMA raster tricks.
Emulating in bsnes
When per-scanline emulation is enough, when only per-dot rendering will do, and the debug tools that let you see a real PPU thinking.
2D graphics from zero
This first part is pure fundamentals — nothing about any specific chip yet. It's the toolkit that every tile-based 2D machine is built from, from arcade boards to the Game Boy to the console we meet in Part II. We build the vocabulary one word at a time — pixel, palette, tile, bitplane, tilemap, sprite, layer, scanline — and each idea has a live lab you can poke. If a term is ever used before it's explained, that's a bug. Everything in Parts II and III is just these ideas wired together in silicon.
Pixels & the framebuffer
Lean in close to any display — a phone, or the family CRT the Super Nintendo was plugged into — and the image falls apart into a grid of tiny glowing dots. Each dot is a pixel (from “picture element”), and it can show exactly one colour at a time. Step back and your eye fuses the grid into a smooth image. That's all a digital picture is: a rectangular grid of single-colour dots.
The whole grid of colour numbers, held in memory, is the framebuffer. The number of dots across and down is the resolution. And here is the first genuinely Super-Nintendo fact: its standard picture is a mere 256 × 224 pixels. That is tiny — about a thirty-sixth of a modern 1080p frame. The console could also switch to a 512-pixel-wide hi-res width, output 239 tall, or run interlace to reach 448/478 tall, but the everyday canvas artists worked on was those 256 × 224 dots. Every trick in this course exists to make that small grid look rich. And here's a twist to file away right now: the SNES famously does not store this grid — there is no big framebuffer of finished pixels inside the console. Part II shows how it fakes one, a line at a time.
A mosaic portrait is thousands of small single-colour tiles. Up close: coloured squares. Across the room: a face. A pixel grid is a mosaic your machine repaints sixty times a second — and the framebuffer is the numbered order sheet saying which tile goes where. On the SNES that sheet was small enough to matter: every pixel had to earn its place.
Play with the idea below. Pick a colour, drag across the grid, and watch a picture become a list of numbers. Shrink the grid and every dot gets chunkier; the byte count in the readout is the size of this little framebuffer. A grid-of-pixels image like this is called a raster image — remember the word, because Module 07 is all about how the beam rasterises one line at a time.
- A digital image is a grid of pixels; each pixel shows one colour.
- The framebuffer is the memory holding those numbers — change it and the screen changes.
- The SNES standard resolution is a small 256 × 224 (also 512-wide hi-res, 239 tall, and interlaced 448/478).
- On the SNES a pixel is stored as an index, not raw RGB — the next module explains why.
Colour & palettes
Storing three full bytes of red/green/blue for every one of 57,344 pixels would have been ruinously expensive in 1990. So the SNES — like almost every machine of its era — uses indexed colour. Each pixel holds a small index (a 4-bit number 0–15 is typical), and a separate table turns that index into a real colour. That table lives in a dedicated 512-byte memory called CGRAM, and it holds 256 entries.
Reading binary and hex — a 60-second detour.
A bit is a single 0-or-1; a group of n bits can
count from 0 up to 2n−1, so 5 bits reach 31 and 8 bits
reach 255. Hardware people write binary with a %
prefix and hexadecimal (base 16, digits 0–9 then A–F) with a
$ prefix. One worked conversion:
%11111 is 16+8+4+2+1 = 31, which in
hex is $1F (one 16 plus fifteen 1s). That's all the
notation this course ever uses.
Each CGRAM entry is a 15-bit BGR value: five bits of
blue, five of green, five of red, packed into one 16-bit word (the
top bit is unused). Five bits per channel means 32 shades each, so
the SNES's total palette — the set of colours it can choose
from — is 32 × 32 × 32 = 32,768
colours. It can display up to 256 of them at once (before
the colour-math trickery of Module 12). Note the channel order:
hardware people write it BGR because blue occupies the high
bits — a colour word looks like 0bbbbbgggggrrrrr.
(To the CPU, CGRAM is a little port: you pick an entry number at
$2121, then send the 15-bit word as two writes to
$2122 — low byte first, then high.)
One more idea completes the picture: sub-palettes. A pixel's index is usually only 4 bits (0–15), yet CGRAM holds 256 colours. The bridge is slicing: CGRAM is carved into sixteen 16-colour blocks, and every tile or sprite is told (by its tilemap entry or OAM attributes — Modules 04 and 05) which block its 0–15 indices point into. By convention the first eight blocks serve the backgrounds and the last eight serve sprites. Module 03's tiles will produce exactly those 0–15 numbers — now you know what they index into.
$2122 as two byte writes — low half first. Bottom: the 256 entries behave as sixteen 16-colour sub-palettes; a 4bpp tile's pixel indices (0–15) only choose a column, while its tilemap entry or OAM attribute chooses the row. Backgrounds use the first eight rows, sprites the last eight.A paint-by-numbers kit gives you a canvas full of little numbered regions and a legend: “3 = sky blue, 7 = grass green.” The canvas never stores paint — just numbers. Swap the legend and the whole picture recolours without touching the canvas. That legend is CGRAM, and swapping it is exactly how SNES games flash, fade to white, or cycle waterfalls — change the palette, not the pixels.
The lab below is a colour mixer over one 16-entry sub-palette. Drag the R/G/B sliders (each 0–31) to build a colour and read its 15-bit word live; click any swatch to select and edit that entry. This is, almost exactly, what a game's palette editor showed an artist in 1991.
- Pixels store an index; CGRAM (512 bytes, 256 entries) turns each index into a colour.
- Each entry is 15-bit BGR — 5 bits per channel — so the master palette is 32,768 colours.
- CGRAM is sliced into sixteen 16-colour sub-palettes: a tile's 0–15 indices pick a column, its tilemap/OAM entry picks the row.
- Up to 256 colours on screen at once from that palette (more with colour math).
- Recolouring the whole scene = rewriting CGRAM, not the pixels — the source of countless SNES effects.
Tiles & bitplanes
The SNES almost never draws individual pixels. It draws tiles: fixed 8 × 8 blocks of pixels, stored once and reused all over the screen. A brick wall is one brick tile repeated; a font is a tile per glyph. Reuse is the whole point — it slashes memory and lets the hardware fetch a known-size block on a predictable schedule.
Now, how is one tile's colour data laid out? Not as “index, index, index” pixel by pixel. Instead the SNES uses planar bitplanes. Think of a pixel's index in binary. A 4-bit index has bits 0,1,2,3. The hardware stores all the bit-0s of every pixel as one 8 × 8 one-bit image (plane 0), all the bit-1s as another (plane 1), and so on. Stack the planes and read the same pixel out of each: those bits, combined, are that pixel's palette index. The number of planes sets the bit depth: 2bpp (2 planes → 4 colours), 4bpp (4 planes → 16 colours), or 8bpp (8 planes → 256 colours). Why so roundabout? Because it matched the silicon of the day: a one-bit-wide shift register per plane was about the cheapest circuit a 1980s chip designer could lay down, so spitting out one bit from each plane per pixel cost almost nothing.
Try one row by hand to make it concrete. Suppose a 2bpp row's two
plane bytes are $3C and $66 — in binary,
plane 0 = %00111100 and plane 1 = %01100110.
Read each column top to bottom (plane 1's bit is the twos place,
plane 0's the ones place) and the eight pixels come out as indices
0, 2, 3, 1, 1, 3, 2, 0
— which, via Module 02, are eight colours from the tile's
sub-palette. One exception to file away for Module 10: Mode 7's
tiles are not planar at all — they're stored linear, one
whole byte per pixel.
Imagine four sheets of clear acetate, each stamped with a pattern of dots. Lay them on top of one another over a lightbox: where sheets 1 and 3 both have a dot, you read the binary number 0101 = 5, and pixel 5's colour appears. Toggle one sheet and every pixel's number shifts. That's a bitplane tile — and it's exactly what the lab lets you do.
In the lab, paint palette indices onto the 8 × 8 tile, then switch individual planes off. Watch the composited colours jump as a bit is forced to zero everywhere — and watch the raw 32-byte layout update underneath. This is the exact byte order a real SNES tile occupies in VRAM.
- Everything is built from reusable 8×8 tiles — huge memory savings.
- Tiles are stored as planar bitplanes: one 1-bit layer per index bit, stacked.
- Bit depth = number of planes: 2bpp/4bpp/8bpp → 4/16/256 colours.
- A 4bpp tile is 32 bytes, with planes 0/1 interleaved by row, then 2/3.
Tilemaps & scrolling
A tile is 8 × 8; the screen is far bigger. The bridge is the tilemap: a grid of small entries, one per tile-sized cell of the background. A SNES tilemap screen is 32 × 32 entries (256 × 256 pixels), and layers can stitch several of these together for scrolling room — each background chooses one, two or four screens, arranged 32×32, 64×32, 32×64 or 64×64 tiles. Each entry is a 16-bit word that packs more than just a tile number: a tile index, a palette number (which of the sub-palettes to use), a priority bit, and horizontal & vertical flip flags — so one tile can face four ways and wear different colours across the map for free.
Scrolling is where tiles earn their keep. To move the whole background, the SNES doesn't shuffle pixels — it just changes two numbers: the background's horizontal and vertical scroll offsets (BGnHOFS / BGnVOFS). The PPU adds those offsets when it decides which tile and pixel to fetch, and the map wraps around at its edge, so a small map can scroll forever. Increment the offset each frame and the world glides past.
Picture a long roll of patterned wallpaper behind a wall with a small window cut in it. You never move the paper's pattern pixel by pixel — you just slide the whole roll left or right behind the window, and different parts show through. The scroll registers slide the roll; the window is your 256 × 224 screen.
Below, a 32 × 32 map built from four procedural tiles scrolls behind a viewport. Drag the scroll sliders or hit auto-scroll and watch the wrap-around: the map is only 256 pixels wide, yet it never runs out.
- A tilemap is a 32×32 grid of entries; each entry names a tile plus palette, priority and H/V flip.
- One tile can be reused in four orientations and several palettes across the map.
- Scrolling is just two registers per layer — the PPU offsets its fetches; nothing moves in memory.
- Maps wrap around, so a 256-pixel map scrolls endlessly.
Sprites / OBJ
Backgrounds are locked to their tile grid. But Mario, a fireball, a
coin — these need to move to any pixel, independent of the grid. Those
are sprites,
which the SNES calls OBJ. A sprite is still made of
tiles, but it isn't pinned to a tilemap cell: it carries its own
(x, y) position and is composited on top of the
backgrounds wherever you place it.
Everything the PPU needs to draw the sprites lives in a small dedicated memory called OAM. Per sprite it stores the X and Y position, the starting tile number, a palette selection, a priority (which lets a sprite sit in front of or behind particular backgrounds), and horizontal/vertical flip flags — the same attribute vocabulary as a tilemap entry, but now free-floating. Change a sprite's X in OAM and it slides across the screen without a single pixel being redrawn by hand.
Old TV animation used a painted background and separate felt or cel figures laid on top, nudged frame by frame. The background never changed; only the little figures moved. Sprites are those figures, and OAM is the director's list of where each one sits this frame.
In the lab, sprites bounce over a tiled background. Add more with the slider, and flip the priority so the OBJ layer ducks behind the ground — a trick games use constantly for grass, doorways and water.
- Sprites (OBJ) are tile-built objects placed at any pixel, over the backgrounds.
- OAM (544 bytes) holds each sprite's position, tile, palette, priority and flip.
- Moving a sprite = editing a couple of OAM bytes; no pixels are pushed around.
- Priority lets sprites weave in front of or behind chosen background layers.
Layers & priority
A SNES scene is several background layers plus the sprite layer, composited front-to-back into the final picture. Depending on the mode (Module 09) there can be up to four backgrounds — BG1, BG2, BG3, BG4 — and each can scroll independently. Parallax skies, foreground bushes, a HUD that stays put: all just separate layers moving at different speeds.
Two rules turn a stack of layers into a picture. First, transparency: in every layer, palette colour 0 is transparent, so lower layers show through wherever an upper layer's pixel is index 0. Second, priority: beyond the base layer order, individual tiles and sprites carry priority bits that can promote them above layers that would otherwise cover them. That's how a sprite can pass behind a pillar (low priority) and then in front of the floor (high priority) in the same scene.
One refinement worth getting right: the sprite layer isn't a single sheet that always sits on top. Each sprite carries a priority of 0–3, and those four levels are interleaved with the background priorities — a priority-0 sprite slips behind every background, and a middling one can hide behind BG1 while still covering BG2. In our pillar scene, the pillar is a BG1 tile with its priority bit set; the hero at OBJ priority 2 ducks behind it, while the floor is BG1 with priority clear, so the same hero walks in front of that. And one famous special case: in Mode 1 (Module 09), BG3 tiles with their priority bit set jump in front of everything, sprites included — that's how games kept HUDs and text boxes unbudgeable on top of the action.
A pop-up book is cut sheets standing at different depths: sky at the back, hills in the middle, characters up front, with holes where you see through to the layer behind. The SNES builds each frame the same way — stacked sheets with transparent gaps — except it can re-shuffle which sheet stands in front, tile by tile.
Toggle the four layers below and watch the composite rebuild. Turn off BG1 and the ground vanishes to reveal the hills behind; turn off the OBJ layer and the hero disappears. Colour 0 in each layer is the see-through ink that makes it all fit together.
- A frame is several background layers plus sprites, composited front-to-back.
- Colour index 0 in a layer is transparent — lower layers show through.
- Priority decides who wins where layers overlap: base order, per-tile bits, and four OBJ levels interleaved with the BGs — Mode 1's promoted BG3 beats everything (the HUD trick).
- Independent per-layer scrolling gives parallax and stationary HUDs.
Raster timing
The SNES doesn't compute a whole frame and then show it. It feeds a CRT, where a single electron beam sweeps left-to-right along one scanline, then snaps back and drops to the next line, top to bottom. The PPU produces the picture the same way — one scanline at a time, in lockstep with the beam. An NTSC frame is 262 scanlines of which 224 are visible, each line lasting 341 dot-clocks of which 256 are visible; the machine repeats it roughly 60 times a second.
Between the useful parts are two pauses. After each visible line the beam races back to the left — the horizontal blank (H-blank). After the last visible line the beam returns to the top — the longer vertical blank (V-blank): on NTSC that's lines 224–261, about 38 lines ≈ 2.4 ms of every 16.7 ms frame. These gaps matter enormously, because the PPU is reading VRAM the entire time it's drawing. If the CPU writes VRAM mid-line, the PPU owns the memory and the write is simply lost, or lands at the wrong address — your data never arrives, or corrupts a spot you didn't aim at. So the rule, burned into every SNES programmer's brain, is: touch VRAM, CGRAM and OAM only during blanking — mostly the roomy V-blank. (There is one escape hatch: forced blank, a register bit that turns the display off entirely, making every moment safe — it's how the very first screenful of graphics gets loaded behind a black screen.)
A theatre crew can't repaint the set while the actors are performing under the lights — they wait for the blackout between scenes. V-blank is that blackout: the one moment the “stage” (VRAM) is dark and the crew (the CPU) can rush in and change things before the lights come back up for the next frame.
The lab runs a beam across a full raster. The shaded right margin and bottom band are H-blank and V-blank; the readout tells you, at every moment, whether it's safe to write video memory. Slow it down to watch a single line being painted dot by dot.
- The picture is produced scanline by scanline, in step with a CRT's electron beam.
- H-blank follows each line; V-blank follows the frame — the gaps between drawing.
- The PPU reads VRAM while drawing, so the CPU must write VRAM/CGRAM/OAM only during blanking.
- V-blank is the main window for bulk video-memory updates — and at 38 lines (≈2.4 ms) per NTSC frame, it's short.
- Forced blank ($2100 bit 7) switches the display off and makes writes safe any time — how the first screenful ever loads.
The SNES PPU
Now we open the box. The Super Nintendo's graphics are produced by two chips working as one — the S-PPU1 and S-PPU2 — reading from three small memories on a strict per-scanline schedule. In this part we map the pipeline, walk the eight background modes, unpack Mode 7's rotating floor, hit the real sprite limits, blend the main and sub screens with colour math, and abuse HDMA to change registers mid-frame. Everything from Part I is about to become a register.
The two PPUs & the pipeline
The SNES splits picture generation across two chips. The
S-PPU1
is the renderer: it contains the background and OBJ engines, fetching
tiles from VRAM and evaluating OAM to decide which sprites appear on
each scanline. The
S-PPU2
is the finisher: it applies windows and colour math, looks the winning
pixel up in CGRAM, and drives the final video output. They pass pixel
data between them every dot; to a programmer they behave like one unit
addressed through a bank of registers at $2100–$213F.
A word on what those “registers” physically are, because
every module from here on names them. $2100–$213F are
memory-mapped I/O:
addresses that are wired straight to the PPU chips rather than to any
RAM. When the CPU writes to $2105, no memory cell changes —
the value lands on the PPU's control lines and the chip changes
behaviour, instantly. A register write is a command, not a
stored byte. That's why when you write one (this dot, this
line, this frame) matters as much as what you write.
Between them the pair owns three dedicated memories, and their sizes are worth memorising because every later module bumps into them: VRAM 64 KB (tiles and tilemaps), CGRAM 512 B (the 256-colour palette), and OAM 544 B (the 128 sprites' attributes). None is large. Fitting a game's graphics into 64 KB of VRAM, streamed in during those short blanking windows, was the central art of SNES development.
Because the pipeline runs per dot with no frame buffer of finished pixels to fall back on, everything that changes the picture must be timed against the beam. That single fact powers Modules 10, 12 and 13 — and makes accurate emulation (Part III) genuinely hard.
- Two chips: S-PPU1 (the BG and OBJ rendering engines, VRAM/OAM fetch) and S-PPU2 (windows, colour math, CGRAM lookup, video output).
- Three small memories: VRAM 64 KB, CGRAM 512 B, OAM 544 B.
- Programmed through registers $2100–$213F; they behave as one unit.
- The pipeline emits one pixel per dot with no finished-frame buffer — timing is everything.
The background modes
The PPU's background engine — it lives in S-PPU1, and register
$2105 drives it — offers eight
background modes,
numbered 0–7. A mode decides how
many BG layers you get and how deep each one's colour is. There is no free
lunch: bits spent on more colours are bits not spent on more layers.
Mode 0 gives the most layers — four BGs, but each only
2bpp (4 colours). Mode 1 is the workhorse the vast
majority of games use: BG1 and BG2 at 4bpp (16 colours) plus a 2bpp BG3,
a great balance of colour and layer count — and BG3 tiles with their
priority bit set jump in front of everything, which is why Mode 1's
“spare” layer so often carries the HUD (Module 06).
Mode 3 trades
layers for a single 8bpp (256-colour) BG1. Modes 5 and 6
switch to true 512-pixel hi-res — the PPU genuinely fetches and outputs
double the horizontal detail. (Don't confuse that with
pseudo-hi-res,
a separate $2133 trick that interleaves the main and sub
screens in any mode.) True hi-res stayed rare: fetching twice the
pixels eats twice the VRAM bandwidth, and on a blurry consumer CRT the
extra columns mostly smeared away. Modes 2, 4 and 6 add
offset-per-tile,
letting columns scroll independently. And Mode 7 is a
special case entirely — one layer you can rotate and scale — so it gets
its own module next.
| Mode | Layers | Depths | Notable for |
|---|---|---|---|
| 0 | BG1–4 | 2·2·2·2 bpp | Most layers, 4 colours each |
| 1 | BG1–3 | 4·4·2 bpp | The workhorse — most games |
| 2 | BG1–2 | 4·4 bpp | Offset-per-tile |
| 3 | BG1–2 | 8·4 bpp | 256-colour BG1 |
| 4 | BG1–2 | 8·2 bpp | 256-colour + offset-per-tile |
| 5 | BG1–2 | 4·2 bpp | 512-wide hi-res |
| 6 | BG1 | 4 bpp | Hi-res + offset-per-tile |
| 7 | BG1 | 8 bpp | Rotation / scaling (Module 10) |
Imagine a fixed jar of coins to split between “number of layers” and “colours per layer.” Mode 0 buys four thin layers; Mode 3 blows most of the jar on one gorgeous layer. Choosing a mode is choosing how to spend the jar for this screen — and games switch modes between screens, or even mid-frame, to spend it differently.
Pick a mode below to see its layer line-up and colour budget. The bar width hints at each layer's colour count; OBJ (always 4bpp, 16 colours per palette) rides on top in every mode.
- Eight modes (0–7) trade layer count against colour depth; register $2105 selects one.
- Mode 0 = four 2bpp layers; Mode 1 (4/4/2 bpp) is the everyday choice; Mode 3 = one 8bpp layer.
- Modes 5/6 are true 512-wide hi-res (pseudo-hi-res is a separate $2133 trick); modes 2/4/6 add offset-per-tile.
- Mode 7 is the odd one out — a single rotatable/scalable layer.
Mode 7
Mode 7 is the effect that defined the SNES. It gives up every other background to grant one layer a superpower: the hardware can rotate and scale it per pixel. That's the spinning racetrack of F-Zero, the tilting world map of Super Mario World, the swooping intro of countless games.
The magic is an affine transform.
For every screen pixel, the PPU asks “which pixel of my map
lands here?” and answers with a tiny matrix multiply. Don't let
the word scare you: multiplying a coordinate pair by a 2×2 matrix
just means applying one fixed scale-and-rotate recipe —
map x = A·x + B·y, map y = C·x + D·y —
to every pixel's coordinates. The four numbers live in registers
$211B–$211E — call them
A, B, C, D — stored as
1.7.8 fixed-point
(a signed value times 1/256), and a centre pivot
(X0, Y0) lives in $211F/$2120
(M7X/M7Y).
Mind the direction, because it trips everyone up: the matrix maps
screen → map, not the other way round. For
each screen dot it computes where to sample the map — so
bigger matrix values stride across the map faster, which
zooms out. To magnify, you make the values
smaller: a rotation by θ at a given zoom is
A=cos·θ ÷ zoom, B=−sin·θ ÷ zoom,
C=sin·θ ÷ zoom, D=cos·θ ÷ zoom.
A worked fixed-point example: at θ = 0° and zoom 1×,
A is exactly 1.0, which in 1.7.8 form (1 sign bit, 7
integer bits, 8 fraction bits) is 1 × 256 =
256 = $0100 — the value games write
for “no zoom”. Housekeeping details: each Mode 7 register
is fed its 16 bits as two 8-bit writes, low byte then
high; the map is 128×128 tiles of 8×8 pixels =
1024×1024 px, and its tiles are stored linear, one byte
per pixel — not planar like Module 03's; and when the
sampling point runs off the map's edge, $211A decides the
screen-over
behaviour — wrap around, go transparent, or fill with tile 0.
That's the whole trick: four numbers, updated once per frame, spin an
entire world.
Lay a big printed map on a turntable and look down at a fixed window cut in a card above it. Spin the turntable and the map rotates under your window; slide it closer and it magnifies. Mode 7's matrix is the turntable's angle and zoom — and because it recomputes per pixel, the window can look at the map from any angle without ever redrawing it.
Now the famous part. Rotation and scaling alone give a flat, top-down spin. To get the F-Zero floor receding to a horizon, games rewrite the matrix on every scanline using HDMA (Module 13): lines near the bottom use a larger scale (close), lines near the top a smaller one (far). The flat map bends into a perspective plane — a 3D illusion from a strictly 2D chip. Flip the perspective toggle in the lab to see it snap into place.
- Mode 7 is one background the PPU can rotate and scale per pixel via a 2×2 affine matrix.
- Matrix A/B/C/D live at $211B–$211E as 1.7.8 fixed-point (each fed as two writes, low byte then high); the centre pivot M7X/M7Y is at $211F/$2120.
- The matrix maps screen → map, so bigger values zoom out; magnifying means writing values below $0100 (A = cos ÷ zoom).
- The map is 128×128 tiles (1024×1024 px), stored linear at 1 byte/pixel — not planar; $211A picks the off-map behaviour (wrap, transparent, or tile 0).
- Rewriting the matrix per scanline with HDMA fakes perspective — the racing-floor effect.
Sprites on hardware
Module 05 introduced sprites conceptually; here are the hardware numbers. The SNES can track 128 sprites at once in OAM. Each frame you pick two sizes from the size table (say 8 × 8 and 16 × 16, or 16 × 16 and 32 × 32) via register OBSEL ($2101), and each sprite is flagged as the small or large one. Sprites are always 4bpp — sixteen colours — and always draw from the top half of the palette, CGRAM entries 128–255 (the eight OBJ sub-palettes from Module 02). So far so generous.
The catch is per-scanline. As the beam draws each line, the S-PPU1 has only so much time to fetch sprite data, and the limits are hard: at most 32 sprites may appear on any one scanline, and at most 34 tiles' worth (8 × 8 slices) of sprite pixels can be fetched for it. Exceed the sprite count and the PPU raises the range-over flag and drops the extras; exceed the tile budget and the time-over flag fires and sprites lose slices. The visible result is the classic SNES flicker — sprites winking out when too many crowd one line. Why two limits? They guard two different resources: the 32 is evaluation slots (how many OAM entries the PPU can even consider for a line), the 34 is fetch slivers (how many 8-pixel slices of tile data it has time to read from VRAM). Bigger sprites burn slivers faster — a 32-pixel-wide sprite costs 4 slivers per line, so just eight of them side by side exhaust the sliver budget while using only a quarter of the evaluation slots.
You can invite 128 guests to the party (OAM), but only 32 fit through the doorway at once (per scanline). Send more and the ones at the back get turned away this trip. Games manage the crowd by cycling which sprites get priority each frame — that's why old flicker shimmers rather than simply vanishing.
The lab piles sprites onto the screen and enforces the real limits line by line. Dropped sprites are shown faint so you can see exactly where the beam ran out of budget. Hit “pile on one line” to force the ceiling.
- 128 sprites total in OAM; two selectable sizes per frame (OBSEL $2101); always 4bpp, from CGRAM entries 128–255.
- Per scanline: 32 evaluation slots and 34 fetch slivers — wider sprites eat slivers faster (a 32-wide sprite is 4 per line).
- Over the limit raises range-over / time-over flags and drops sprites — visible as flicker.
- Games rotate sprite priority each frame to spread the flicker out.
Colour math
The SNES has no per-pixel alpha channel. It gets translucency a cleverer
way: it composes the scene twice. Layers are each assigned to the
main screen
or the sub screen,
producing two pictures, and then colour math combines them
pixel by pixel. The assignment is just two bitmask registers:
$212C enables layers on the main screen, $212D
on the sub screen. Concretely, a game wanting a translucent waterfall
sets the bits so BG1 (the level) and OBJ (the hero) land on the main
screen while BG2 (the waterfall) goes to the sub screen — then asks
for add-half wherever the waterfall's pixels are.
The operation is simple arithmetic per channel: add (main + sub, clamped to 31) or subtract (main − sub, clamped to 0), each with an optional half that divides the result by two. Two registers steer it all: CGWSEL ($2130) decides where math may happen, and CGADSUB ($2131) picks add/subtract, the half option, and which layers join in. Add-half of a bright layer over the scene gives a soft glow; subtract darkens for shadow; a plain add of a coloured pane makes stained glass. When there's no sub-screen layer behind a pixel, a fixed colour stands in — perfect for fading the whole frame to black or white. And windows can restrict where the math happens, carving spotlights and iris wipes.
One concrete blend, worked in numbers. A brick-red wall pixel on the main screen is (R,G,B) = (20, 10, 8); a pane of blue glass on the sub screen is (6, 14, 28). Choose add-half and the S-PPU2 computes each channel as (main + sub) ÷ 2: R = (20+6)/2 = 13, G = (10+14)/2 = 12, B = (8+28)/2 = 18 — a dusty blue-grey (13, 12, 18), exactly what bricks seen through 50% blue glass should look like. Dial those same six numbers into the lab below and watch the result swatch agree.
Shine two slide projectors at the same screen. Where both land, the light adds and you get a brighter blend; dim one and the mix shifts. Colour math is that second projector — the sub screen — and the “add / subtract / half” knobs decide how the two images combine.
Mix a main and a sub colour below, choose an operation, and read the resulting 15-bit word. This is precisely the per-channel maths the S-PPU2 runs for every pixel where colour math is enabled.
- No alpha channel — the SNES composes a main screen ($212C) and a sub screen ($212D) and blends them.
- Colour math adds or subtracts per channel, optionally halved, clamped to 0–31 — steered by CGWSEL ($2130) and CGADSUB ($2131).
- A fixed colour substitutes for the sub screen where no sub layer exists — used for full-frame fades.
- Windows restrict where the math applies, giving spotlights and shaped transitions.
HDMA raster tricks
A PPU register normally holds one value for the whole frame. But the beam draws the frame one line at a time, and between lines there's an H-blank. What if a register held a different value on every line? HDMA — horizontal-blank DMA — does exactly that. You hand it a small table, and the hardware feeds one entry to a chosen register during each line's H-blank, all frame, with the CPU free to do other work.
That one capability is behind a startling number of SNES effects. Feed the backdrop colour register a new value each line and you get a smooth sky gradient from a machine with no gradient hardware. Feed a BG scroll register a wobbling offset and flat water ripples. Step the mosaic register and the screen pixelates from top to bottom for a dissolve. And, as we saw, feed the Mode 7 matrix a new scale each line and a flat plane bends into a 3D floor. HDMA is how the SNES turned strict per-scanline timing from a constraint into a paintbrush.
A player piano reads a punched roll: at each moment the roll tells the keys what to do, no pianist needed. An HDMA table is that roll, and the “moment” is each scanline's H-blank. You punch the pattern once; the hardware plays it down the whole frame, line after line.
The studio below applies a per-scanline effect to a canvas. Switch between a gradient backdrop, a per-line scroll ripple and a growing mosaic — each is one register being rewritten ~200 times a frame, exactly as HDMA would.
- HDMA writes a table of values to PPU registers, one entry per scanline, during H-blank.
- Per-line backdrop colour → gradients; per-line scroll → ripples; per-line mosaic → dissolves.
- Per-line Mode 7 matrix → the perspective racing floor.
- It runs autonomously, freeing the CPU — the SNES's signature source of “how did they do that?”.
VRAM, CGRAM, OAM & the port bottleneck
The three PPU memories aren't on the CPU's address bus — the CPU can't just
write VRAM location 5,000 directly. It goes through ports:
special registers you poke in sequence. (Two units first, since we'll count
in both: a byte is 8 bits, and a word here
means 16 bits — two bytes. VRAM is organised as 32,768 words.) To fill VRAM
you first set the word address with $2116/$2117 (low/high byte),
choose how the address should
auto-increment
(by 1, 32, or 128 words, via $2115, which can also remap the
address bits for bitmap-style uploads), then stream data through
$2118/$2119. Those are one-byte ports:
$2118 carries a word's low byte and $2119 its high
byte, so a pair of writes delivers one 16-bit word — and bit 7 of
$2115 selects which of the two ports' writes actually bumps the
address pointer. CGRAM has its own pair
($2121 address, $2122 data — Module 02); OAM uses
$2102–$2104.
Here's the squeeze. Writing through a port is one value at a time, and
(Module 07) you may only do it during blanking. An NTSC V-blank is 37–38
lines — roughly 6,300–8,600 CPU cycles depending on speed — which sounds
roomy until you price it out: it's enough to push only about
6 KB into VRAM per frame, and far less if the CPU
hand-writes each byte in a loop. The answer is
DMA:
a hardware block-copier that streams bytes to the data port at bus speed.
On the SNES, DMA into VRAM during V-blank isn't a luxury — it's the only
way to get graphics in on time; a full 64 KB VRAM still takes a dozen
frames of V-blanks. (Its per-scanline cousin HDMA powered Module 13. And
when a game can afford a black screen — a loading moment, a scene change —
it flips on forced blank, $2100 bit 7, and loads everything
at leisure with the display off.)
The simulator below is the VRAM port in miniature. Set the address, pick an increment, then write bytes or auto-fill a burst and watch the pointer march through VRAM. Change the increment to 32 and see the writes stride down a column instead of along a row — exactly how you'd load a tall strip of tiles.
- The CPU reaches VRAM/CGRAM/OAM only through I/O ports, not directly.
- VRAM: set $2116/7 address, choose increment on $2115, stream data via the one-byte ports $2118/9 — a pair of writes per 16-bit word, $2115 bit 7 picking which port bumps the pointer.
- CGRAM uses $2121/$2122; OAM uses $2102–$2104.
- Blanking-only writes plus a ~6 KB-per-V-blank ceiling make DMA the essential way to load graphics; forced blank ($2100 bit 7) lifts the ceiling by switching the display off.
Emulating in bsnes
You now know how a real SNES makes a picture. This final part asks the reverse question: how do you rebuild that behaviour in software, faithfully? The answer turns on a single decision — how finely you simulate the beam — and it's the fault line between an emulator that runs almost everything and one that also nails the last few CPU-timed, mid-scanline stunts. Then we point you at the debug tools that let you watch a live PPU think.
Per-scanline vs per-dot
An emulator has to decide how often to re-check the PPU's registers. Per-scanline rendering reads the registers once per line and draws all 256 dots with those values. Here's the part people get wrong: that is enough for HDMA. Module 13's writes land in H-blank, between lines — so a per-scanline renderer that applies HDMA at each line boundary reproduces gradients, ripples and Mode 7 perspective floors perfectly. That's exactly why the fast approach works for the overwhelming majority of games.
What it can't see is a register change landing in the middle of the visible line — a CPU-timed mid-display write. The canonical example is Air Strike Patrol, which redraws its runway shadow by poking the PPU partway across specific scanlines: a per-scanline renderer already committed those lines, so the shadow simply vanishes. Add a handful of fine latching quirks (exactly which dot a scroll or window value is sampled at) and you have the genuine case for per-dot rendering: advance the PPU one pixel-clock at a time, applying every write at the precise dot it occurred. The cost is speed — many more checks per frame. The landscape today: bsnes defaults to a fast scanline PPU, with per-dot cycle rendering as higan / bsnes's accuracy option, while Mesen2 renders per-dot outright.
Glance once as your carriage passes a sign and you catch a single word; read it continuously and you get the whole message. Per-scanline emulation glances once per line; per-dot reads the sign the whole way past. If the sign only changes between carriages (HDMA in H-blank), one glance per carriage is plenty — but for a sign repainted mid-carriage, a CPU write in mid-display, only the continuous reader gets it right.
Both panels below run the same raster stunt: a CPU hammering a colour register repeatedly during each visible line (this is deliberately not HDMA — HDMA would look identical in both panels). The per-dot panel applies every write at the exact dot it lands, revealing slanted bands the writes paint across the frame. The per-scanline panel only samples the register once at each line's start, so the mid-line detail collapses into a single wavy edge. That residue is the whole argument for per-dot accuracy.
- Per-scanline rendering reads registers once per line; per-dot reads them every pixel.
- HDMA lands in H-blank, between lines — per-scanline rendering reproduces it correctly; only CPU-timed mid-display writes (Air Strike Patrol's runway shadow) and fine latching quirks need per-dot.
- Modern bsnes defaults to a fast scanline PPU; per-dot cycle rendering is higan / bsnes's accuracy option, and Mesen2 renders per-dot.
- The trade is accuracy versus speed — and it's only visible the moment a game changes a register mid-line.
Accuracy & try it
The best way to make all of this permanent is to watch a real game's PPU state while it runs. Modern accuracy-focused emulators ship debuggers that expose exactly the structures this course built up. In Mesen2 or bsnes-plus, open the PPU tools and you'll find one window per module:
- Tile / VRAM viewer — the raw 8×8 tiles from Modules 03–04, decoded from bitplanes at the bit depth you choose.
- Tilemap viewer — the 32×32 maps of Module 04, with the scroll registers and per-tile flip/priority laid bare.
- CGRAM / palette viewer — the 256 entries of Module 02, updating live as the game fades and cycles colours.
- OAM / sprite viewer — the 128 sprites of Modules 05 and 11, with their sizes and the per-scanline count that triggers flicker.
- Event / timing viewer — the beam of Module 07 and the HDMA writes of Module 13, plotted against the scanline where they land.
You've learned every trick in the act; the debugger is the X-ray that shows the hands doing them. Load a Mode 7 game and scrub a frame: you'll see the matrix registers change line by line (Module 10 + 13), the palette shift for a fade (Module 12), and sprites drop off a crowded scanline (Module 11) — all the theory, caught in the act.
Two great closing experiments. First, open Mesen2's event viewer on a Mode 7 racer and pause a frame: you'll see the HDMA writes plotted as a neat column landing in the H-blank of every line — Module 13's piano roll and Module 15's timing argument, photographed in the wild. Second, if you can find Air Strike Patrol, run it in bsnes under the default fast PPU and then under the accuracy profile (or in per-dot Mesen2): the runway shadow — drawn by a CPU write in mid-scanline — vanishes in one and appears in the other. You're no longer watching a mystery; you're reading a pipeline you know stage by stage.
Getting the tools is easy: Mesen2 is at mesen.ca, and bsnes at github.com/bsnes-emu/bsnes, both free. For something to inspect, homebrew and hardware test ROMs (widely published by their authors, and by the emudev community) give you plenty of PPU activity to explore while steering well clear of any legal gray areas around commercial game copies.
- You can follow a picture from an 8×8 bitplane tile, through palettes, layers and colour math, to a scanline on a CRT — the whole SNES PPU.
- You know the numbers that shaped every SNES game: 256×224, 15-bit BGR, 8 modes, Mode 7's matrix, 128 sprites / 32-per-line, VRAM 64 KB.
- You can read a PPU debugger as a live map of the S-PPU1/S-PPU2 — and know exactly which effects demand per-dot emulation.
- Come full circle: the little framebuffer you painted in Module 01 is exactly what the beam is still drawing, one dot at a time.