Multi-speed horizontal scrolling using HDMA – one background image, three scroll zones.
No interactive controls. The background scrolls automatically in three horizontal bands at different speeds.
Then open parallax_scrolling.sfc in your emulator (Mesen2 recommended).
Real parallax scrolling uses multiple BG layers at different speeds. This example achieves the same depth effect with a single layer by using HDMA to change the horizontal scroll register at different screen positions.
The screen is divided into three zones:
| Zone | Scanlines | Speed | Visual |
|---|---|---|---|
| Top | 72 lines | +1 px/frame | Stars/sky (distant, slow) |
| Middle | 88 lines | +2 px/frame | Midground pattern |
| Bottom | 64 lines | +4 px/frame | Grass/ground (close, fast) |
The HDMA table lives in RAM so it can be updated each frame:
The hdmaParallax() function configures HDMA mode 1REG_2X on the specified channel, targeting the BG horizontal scroll register. Mode 1REG_2X writes 2 bytes to the same register – the low byte and high byte of the scroll position.
Each frame, the main loop increments the scroll offsets at different rates:
Because the table lives in RAM, the HDMA hardware reads the updated values on the next frame automatically.
BG scroll registers on the SNES are "write-twice" latched registers. You write the low byte first, then the high byte, both to the same address. HDMA mode 1REG_2X handles this automatically by writing 2 bytes per entry to the target address.
The example uses a 64x32 tilemap (512 pixels wide). When the scroll offset exceeds 512, the tilemap wraps seamlessly because the PPU performs modular addressing on the tilemap. This is wider than the screen (256 pixels), so there is always enough off-screen content to scroll into.
In a true multi-layer parallax, BG1 and BG2 have independent scroll registers. But HDMA can rewrite BG1HOFS at different points during the frame, effectively giving each screen zone its own scroll speed. The limitation is that you cannot overlap the zones – each zone is a strict horizontal band.
| File | Purpose |
|---|---|
main.c | HDMA table setup, per-frame scroll updates |
data.asm | Background tiles, tilemap, and palette data |
res/back.png | Source background image (512 pixels wide) |
Makefile | LIB_MODULES := console dma background sprite hdma input math |
[count][lo][hi] entries before the terminator.backgrounds/continuous_scroll – True multi-layer parallax with player controleffects/hdma_wave – Another HDMA technique (per-scanline wave distortion)