The NES was faking the scroll the whole time.

Published on December 4, 2025

Sprite flicker on the NES was visible. You could see it happening, objects strobing in and out, bullets vanishing in busy scenes. The hardware limit was right there on the surface.

Scrolling was the opposite. When it worked, it looked effortless. When it didn’t, you got a weird color flash or a seam across the screen that appeared and vanished before you could focus on it. Almost everything smooth you ever saw on an NES screen was the result of someone wrestling the hardware into cooperation one scanline at a time.

The root issue is VRAM. The PPU has 2 KB of video RAM for background tiles, enough to hold exactly two nametables, or two screens worth of tile layout. The hardware address space is wired for four nametables, so the remaining two addresses have to go somewhere. Cartridges decide: either mirror one of the real nametables across two addresses, addresses, or populate them with additional RAM on the cartridge itself. Most cartridges mirrored, and the mirroring configuration determined the shape of the playfield.

Horizontal mirroring arranges the two real nametables left and right, which makes horizontal scrolling the natural direction. You scroll right, the viewport moves from nametable A into nametable B, new tiles get written into the left edge of B just behind the camera, and the cycle continues. The memory layout cooperates with the direction of motion. Vertical mirroring stacks the nametables top and bottom, which is the natural arrangement for vertical scrollers like Ice Climber. One-screen mirroring collapses everything into a single nametable, which some games used for specific technical reasons but which limits the playfield considerably.

Mario scrolls horizontally because Miyamoto’s team chose horizontal mirroring and designed around it. That’s not the whole story though. The mirroring choice was also informed by what the hardware made easy, and horizontal was easier for reasons beyond just the memory layout.

The attribute table is the other half of the problem. Each nametable has an associated attribute table that controls the color palette for background tiles. The attribute table is coarse. Each entry covers a 4×4 block of tiles, meaning palette changes can only happen on 32×32 pixel boundaries. This is fine for horizontal scrolling because you’re moving through the attribute grid in the direction it’s laid out. But scroll vertically and the attribute grid almost immediately misaligns with the tile grid, producing color seams and palette pops at the boundaries. Vertical scrolling doesn’t just have to move tiles correctly, it has to keep the attribute assignments coherent across a grid that doesn’t divide evenly into the scroll increment. Most teams decided it wasn’t worth fighting.

This is why so many NES games snap the screen when you move vertically. Castlevania cuts to a new fixed screen when you climb stairs. Mega Man breaks the level into areas separated by hard transitions. Kid Icarus flips the screen when you enter a new section. None of that was an aesthetic choice about pacing or tension, though some of those games turned it into one. It was the path of least resistance around a hardware problem that smooth vertical scrolling made much worse.

Games that needed to scroll in both directions, or that needed smooth vertical motion at all, had to get creative. Super Mario Bros. 3 and Kirby’s Adventure both use mid-frame writes to the scroll registers, timed precisely to the scanline counter. The technique works like this: the PPU renders the screen top to bottom, one scanline at a time. If you change the scroll registers mid-render, the PPU uses the new values for everything below that point. So you let the top portion of the screen render with one scroll value, like a fixed HUD, then hit the registers at exactly the right scanline to switch to the scrolling value for the playfield below. The HUD stays put. The background moves. From the player’s perspective it looks like a split-screen effect, but there’s no split-screen hardware. It’s just a very precisely timed register write.

Some mappers added hardware support for this. The MMC3, used by Super Mario Bros. 3 among others, included a scanline counter that could trigger an interrupt at a specific line, which gave developers a reliable way to hit their mid-frame timing without counting cycles manually. This is what made SMB3’s large, smoothly scrolling levels with a stable HUD possible. The mapper was doing the counting in hardware so the game didn’t have to in software.

Some games went further and added extra VRAM on the cartridge itself, populating all four nametable addresses with real memory instead of mirrors. This eliminated the mirroring constraint entirely. You could scroll in any direction without worrying about the memory layout fighting you. It worked, but it was expensive, which is why it wasn’t common. Later mappers got powerful enough to approximate the same effect through bank switching tricks without the extra RAM.

Once you understand all of this, specific design decisions stop looking arbitrary. Mario moves sideways because the memory layout favors it and the attribute table cooperates. Mega Man climbs between rooms rather than scrolling vertically because smooth vertical scroll would produce color artifacts and require careful attribute management that room transitions sidestep entirely. Metroid uses large expanses of black and dark backgrounds because attribute table misalignment is much less visible when the tiles on either side of a boundary are the same dark color.

The NES was always working harder than it looked. The scroll registers were being written at precise moments between scanlines. The attribute table was being managed to keep color boundaries invisible. The nametable just behind the camera was being rewritten one column at a time, staying ahead of the viewport by the narrowest margin the CPU could manage. The games felt smooth because the developers made them feel smooth, against hardware that was not especially interested in cooperating.