This is a fork of matbo87/snes9x_3ds that adds real stereoscopic 3D to SNES games on the Nintendo 3DS. Background layers are separated at different depths using the 3DS parallax barrier display, similar to how SEGA's M2 studio handled their 3D Classics series.
Slide the 3D slider up and SNES backgrounds gain real depth — sprites stay at the screen plane.
- Download
matbo87-snes9x_3ds.3dsxfrom the latest release - Copy to
sd:/3ds/snes9x_3ds/snes9x.3dsxon your 3DS SD card - Launch from Homebrew Launcher
- Load a game and slide the 3D slider up
With the slider at 0, the emulator behaves identically to upstream — zero overhead.
matbo87/snes9x_3ds is an excellent SNES emulator with themes, thumbnails, bezels, cheats, and a citro3D GPU rendering pipeline. This fork preserves all of that and adds a stereoscopic 3D rendering layer on top.
| matbo87 (upstream) | This fork | |
|---|---|---|
| SNES emulation | Full | Full (identical) |
| Themes, thumbnails, bezels, cheats | Yes | Yes |
| Stereoscopic 3D | No | Yes — M2 3D Classics style (BG, OBJ, Mode 7) |
| 3D slider control | No | Yes — physical slider controls depth intensity |
| Per-layer depth tuning | No | Yes — per-game BG0-BG3, OBJ, Mode 7, Backdrop scale gauges |
| Stretch mode compensation | No | Yes — parallax adjusts across all aspect ratios |
| ROM Info dialog | No | Yes — in-menu ROM details |
| Runtime debug logging | No | Yes — toggle in settings, no rebuild needed |
| Slider at 0 | N/A | Identical to upstream (mono fast path) |
The SNES renders graphics in layers — up to four background planes (BG0-BG3), a sprite layer (OBJ), and a backdrop color. Normally these are composited flat onto a single 2D framebuffer. This fork renders each main-screen layer twice (once per eye) with a small horizontal offset to create parallax.
The critical design choice is that this is done entirely on the GPU:
- CPU builds tile vertices once — no extra CPU work compared to mono rendering
- GPU draws main-screen layers twice with different
stereoOffsetgeometry shader uniforms — one pass for the left eye, one for the right - Each eye's result goes to a separate 256x256 texture (
SNES_MAINfor left,SNES_MAIN_Rfor right) - The textures are composited to the 3DS top screen's left and right framebuffers via the parallax barrier
Window/clip regions and sub-screen transparency layers are drawn only once (they don't need stereo separation).
Layer depths are derived from the emulator's own compositing priority values — the depth0/depth1 parameters already used by the DRAW_* macros in gfxhw.cpp. These encode which layers are in front of or behind others, so we reuse them directly as parallax depth factors. No per-game profiles are needed.
For Mode 1 (the most common SNES mode — Super Mario World, Zelda, Mega Man X, etc.):
| Layer | Stereo Effect |
|---|---|
| BG0 (foreground tiles) | Pops toward the viewer |
| BG1 (mid-ground scenery) | Pops slightly toward the viewer |
| BG2 (far background) | Recedes into the screen |
| Sprites (OBJ) | Per-priority depth — OBJ.0 recedes behind BGs, OBJ.3 pops forward |
| Backdrop | Deepest — behind everything |
Depth values change automatically per SNES graphics mode (Modes 0-6). Mode 7 games (F-Zero, Yoshi's Island, Super Mario Kart, Pilotwings) use perspective-based stereo — the geometry shader scales depth by each scanline's Y position, creating a natural vanishing-point effect where the horizon is flat and the foreground has full parallax.
The options menu includes a Stereoscopic 3D section with per-layer scale gauges:
- BG0-BG3 Scale — Control how much each background layer pops forward or recedes (0% = flat at screen plane, 100% = default, 200% = exaggerated)
- OBJ Scale — Sprite layer depth (per-priority: OBJ.0 behind BGs, OBJ.3 in front)
- Mode 7 Scale — Perspective depth for Mode 7 games (F-Zero, Yoshi's Island, etc.)
- Backdrop Scale — Solid color background depth
- Reset 3D to Defaults — Resets all scales to 100%
- Apply 3D settings to all games — Toggle between global and per-game depth profiles
The physical 3D slider on the side of the 3DS controls overall depth intensity. The per-layer scales let you fine-tune which layers are more or less pronounced on a per-game basis.
Stereo parallax is automatically compensated across all display stretch modes (native 256px, 320px, full 400px, Fit 8:7). The offset is normalized by 256/stretchWidth so perceived depth stays consistent regardless of aspect ratio. The Fit 8:7 edge case (runtime sWidth override to 256 when PPU.ScreenHeight >= 239) is handled explicitly.
- Geometry shader uniform:
stereoOffsetat register #5 inshader_tiles.g.pica, applied to projected X coordinates after the projection matrix multiply - Clip-space scaling: The offset is converted from pixel units to clip space (
* 2.0f / 256.0f) since the orthographic projection maps 0..256 to -1..+1 - Right-eye texture redirect:
SNES_MAIN_Ris a texture ID only, not a member of theSGPU_TARGET_IDenum (adding it would overflow the 3-bitBW_TARGETbitfield in the packed render state union). The redirect happens ingpu3dsApplyRenderState()whenGPU3DS.stereoRightEyeis set - Depth buffer isolation: The shared
SNES_DEPTHbuffer is cleared and window_lr clip regions are re-rendered between eye passes, preventing left-eye depth values from contaminating right-eye depth testing - IOD range: 0-3 pixels maximum displacement per layer, controlled linearly by the 3D slider position
- PICA200 ALU limitation: The geometry shader cannot reference uniforms as direct operands in
add— the uniform value ismov'd to a temp register first
| File | Change |
|---|---|
source/shader_tiles.g.pica |
stereoOffset uniform + per-vertex X offset |
source/3dsgpu.h |
Stereo state fields, right-eye redirect in render state application |
source/3dsgpu.cpp |
Uniform registration, gpu3dsSetStereoOffset(), isReal3DS() stack alloc fix |
source/3dsimpl_gpu.cpp |
Depth factor table, layer scale, per-eye draw loop, stretch compensation, diagnostic logging |
source/3dsimpl.cpp |
SNES_MAIN_R texture allocation, depth buffer sharing, per-eye compositing, missing return fixes |
source/3dsmain.cpp |
gfxSet3D(true), stereo settings menu, config persistence, ROM Info dialog |
source/3dssettings.h/cpp |
Per-layer scale fields, UseGlobal resolution, defaults |
source/3dsconfig.h |
Config version bumps (global 1.3→1.4, game 1.1→1.2) |
source/3dssound.cpp |
Null-check-before-dereference fix in sound init |
source/3dsmenu.cpp |
Buffer size consistency fix |
source/3dsui_img.cpp |
Non-fatal VRAM allocation for external UI textures |
Fixes found during development that improve robustness independent of the stereo feature:
- Memory leak in
isReal3DS()— Heap-allocated version strings were never freed; switched to stack allocation - Null pointer dereference in sound init —
leftBuffer/rightBufferwere derived fromfullBuffersbefore the null check - Missing return values —
S9xReadMousePositionandS9xReadSuperScopePositionstubs had no return statement
Minimally. The CPU builds vertices once regardless of stereo mode. The GPU draws main-screen layers a second time, but the PICA200 handles 256x240 tile rendering comfortably. Target is 50-60 FPS with stereo enabled.
Yes. Open the menu during gameplay and scroll to the Stereoscopic 3D section. Each background layer has its own scale gauge (0-200%). Settings are saved per game by default, or you can check "Apply 3D settings to all games" to use a single global profile. The physical 3D slider still controls overall intensity.
It works with all games that use standard tile-based BG modes (Modes 0-6), which covers the vast majority of the SNES library. Mode 7 games (F-Zero, Yoshi's Island, Pilotwings) get perspective-based stereo depth with a dedicated Mode 7 Scale control.
Yes. Slide the 3D slider to 0. The emulator runs an identical mono code path with zero overhead — the eye loop executes once and no stereo uniforms are written.
Both .3dsx (Homebrew Launcher) and .cia (installable title) versions are provided in each release.
Requires devkitARM ($DEVKITARM must be set) and 3ds-libpng:
sudo pacman -S 3ds-libpng # if not already installed
makeOutput: output/matbo87-snes9x_3ds.3dsx and output/matbo87-snes9x_3ds.cia
See the devkitPro installation guide for toolchain setup.
- Game thumbnails (boxart, title, gameplay)
- Border (bezel) and second screen image (cover) for each game
- Themes (dark mode, RetroArch-style)
- Improved cheat menu with no-intro sets
- RetroArch-ish folder structure
- Swap screen and hotkey options
- Graphic modes 0-7, SDD1, SFX1/2, CX4, DSP, SA-1 chip support
- Sound emulation at 32KHz with echo and gaussian interpolation
![]() |
![]() |
| Start screen with game thumbnails | Pause screen with RetroArch theme |
- matbo87 — upstream snes9x_3ds with citro3D overhaul, themes, thumbnails, and UI
- bubble2k — original snes9x_3ds emulator
- ramzinouri — earlier snes9x_3ds fork
- Asdolo — CIA forwarder
- Stereoscopic 3D implementation by f4mrfaux with Claude Code

