2026
TL;DR
I built a scriptable 3D game engine for the Nintendo DS so you can write and run games directly on the console itself. Written in C using libnds, it compiles to a ~100KB .nds ROM that runs at 60 FPS. Features a touch-based code editor on the bottom screen and real-time 3D rendering on the top screen. Ships with a working 3D pong game as the default script.
What is it?
I felt nostalgic for when I made my first games on an old TI-82 graphing calculator. So I tried bringing that whole experience to my Nintendo DS. A complete programming environment you can hold in your hands.
What you see is a scriptable game engine with a custom programming language featuring variables, loops, and conditionals. You write code using the bottom touchscreen, click play, and the game will execute in real-time on the top screen with full 3D rendering.
How it works
At a high level, the engine breaks down into three parts:
1. Top screen: 3D rendering (hardware accelerated)
Uses the DS's 3D hardware to render colored cubes at 60 FPS. Each model has position (X, Y, Z), rotation angle, and color. The camera is fully controllable with position and yaw/pitch angles.
// DS 3D rendering code (C + libnds)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(camX, camY, camZ, // camera position
camX + lookX, camY + lookY, camZ + lookZ, // look target
0, 1, 0); // up vector Each model is drawn with a transform (position + Y-axis rotation), then the cube geometry: one color, six quads (24 vertices).
// Per-model draw calls (from main.c)
for (i = 0; i glColor3b, then 6 faces as GL_QUADS
glColor3b(r * 255/31, g * 255/31, b * 255/31);
glBegin(GL_QUADS);
/* +Z face */
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
/* -Z, +Y, -Y, +X, -X ... (24 vertices total) */
glEnd(); 2. Bottom screen: Script editor (software rendered)
A touch-based code editor with a custom UI drawn pixel-by-pixel to a 256x192 bitmap. Features include:
- Token picker: tap to insert commands (SET, ADD, LOOP, IF_GT, etc.)
- Numpad: edit number parameters for each command
- Register selector: choose which variable (A-Z) to use
- Play/Pause/Stop/Step: control script execution
- 6 script slots: save and load different programs
// Software rendering to bottom screen u16 *subBuffer = (u16*)BG_BMP_RAM_SUB(0); // 256x192 framebuffer subBuffer[y * 256 + x] = RGB15(31, 31, 31); // white pixel
3. Script interpreter
Executes one line of script per frame (~60 lines/sec). Scripts can use 26 variables (A-Z) plus 9 read-only registers for input (D-pad, buttons) and system state (elapsed time, camera direction).
// Script execution (simplified)
if (tokenEquals(script[scriptIP], "add")) {
int r = scriptReg[scriptIP]; // which register (A-Z)
registers[r] += getNumberParamValue(scriptIP, 0);
scriptIP++; // next line
} The scripting language
Scripts are built from tokens (commands) with numeric parameters. Each line executes instantly, with no parsing overhead, just a series of if-checks against token names.
Available commands
Variables & Math
SET A 5— set register A to 5ADD A 1— add 1 to ASUBTRACT A 2— subtract 2 from AMULTIPLY B -1— multiply B by -1
Control Flow
LOOP/END_LOOP— infinite loopIF_GT A 10— if A > 10IF_LT A 0— if A < 0IF_TRUE kA— if A button pressedEND_IF— close conditional
3D Objects
MODEL 0— create model at index 0POSITION 0 X Y Z— set positionANGLE 0 45— set rotation angleNEXT_COLOR 0— cycle color
Camera & Rendering
CAM_POS X Y Z— set camera positionCAM_ANGLE yaw pitch— set look directionBACKGROUND 2— set bg color (0-3)BEEP— play 0.1s soundSLEEP 0.016— pause (60 FPS = 0.016s/frame)
Read-only registers (input & state)
-
LEFT, UP, RGT, DN: D-pad (1.0 when held, 0.0 when released) KA, KB: A and B buttonsTIME: elapsed seconds since script started-
LOOKX, LOOKZ: camera forward direction (normalized X and Z)
Example: 3D pong (default script)
The engine ships with a playable pong game. Here's a simplified excerpt:
MODEL 0 ; create ball
MODEL 1 ; create paddle
CAM_POS 0 8 18 ; position camera
SET A 0 ; ball X position
SET B 1 ; ball velocity
SET C 0 ; paddle Z position
LOOP
ADD A B ; move ball
IF_GT A 10 ; hit right wall?
MULTIPLY B -1 ; reverse velocity
END_IF
IF_TRUE Up ; up button pressed?
ADD C -0.5 ; move paddle up
END_IF
POSITION 0 A 0 0 ; update ball position
POSITION 1 -13 0 C ; update paddle position
SLEEP 0.016 ; ~60 FPS
END_LOOP The full script includes collision detection, game-over logic, and beep sounds on miss, all done with simple register math and conditionals.
Technical details
Language & toolchain
- Language: C
- Library: libnds (Nintendo DS development library)
- Toolchain: devkitPro (ARM cross-compiler)
- Source size: ~3,100 lines of C (main.c)
- Binary size: ~100 KB (.nds ROM)
- Performance: 60 FPS on DS Lite (2006 hardware)
Capabilities & limitations
- Up to 128 script lines per program
- 26 variables (A-Z) + 9 read-only registers
- Up to 16 3D models (simple cubes with color/position/rotation)
- 6 save slots for different scripts
- No dynamic memory allocation, all arrays are statically sized
- No string variables, numbers only (floats)
- No function calls or subroutines (yet!)
How to build & run
Compilation (on your computer)
- Install devkitPro (includes devkitARM and libnds)
- Download the source code (main.c + Makefile)
-
Run
makein the project directory -
Output:
program.nds(~100 KB ROM file)
Running on real hardware
You need a flashcart (e.g. R4, DSTT, Acekard) with a microSD card:
-
Copy
program.ndsto the microSD card - Insert the microSD into the flashcart
- Insert the flashcart into your DS
- Boot the DS and select the ROM from the flashcart menu
Note: I got my R4 cart + SD card from a friend years ago, so I don't have detailed setup instructions for the cart itself. Most modern flashcarts just need you to copy their firmware to the SD root, then add ROMs in a folder.
Try it in your browser (Nintendo DS emulator)
You can test the DS game engine build directly below. The emulator loads ds-game-engine.nds. Loads a more basic pong game than the one in the video.
Nintendo DS emulator (Desmond). If the game doesn’t start, ensure JavaScript is enabled and the page has finished loading.
Download
Compiled ROM (ds-game-engine.nds)
Discussion
Feel free to ask or discuss in this Reddit thread