DS code editor & scriptable game engine

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 < MAX_MODELS; i++) {
    if (!modelActive[i]) continue;
    glPushMatrix();
    glTranslatef(modelX[i], modelY[i], modelZ[i]);
    glRotatef(modelAngle[i], 0, 1, 0);
    drawCube(CUBE_COLORS[modelColorIndex[i]]);
    drawWireframeCube();
    glPopMatrix(1);
}

// Cube geometry: RGB15 color -> 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 5
  • ADD A 1 — add 1 to A
  • SUBTRACT A 2 — subtract 2 from A
  • MULTIPLY B -1 — multiply B by -1

Control Flow

  • LOOP / END_LOOP — infinite loop
  • IF_GT A 10 — if A > 10
  • IF_LT A 0 — if A < 0
  • IF_TRUE kA — if A button pressed
  • END_IF — close conditional

3D Objects

  • MODEL 0 — create model at index 0
  • POSITION 0 X Y Z — set position
  • ANGLE 0 45 — set rotation angle
  • NEXT_COLOR 0 — cycle color

Camera & Rendering

  • CAM_POS X Y Z — set camera position
  • CAM_ANGLE yaw pitch — set look direction
  • BACKGROUND 2 — set bg color (0-3)
  • BEEP — play 0.1s sound
  • SLEEP 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 buttons
  • TIME: 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)

  1. Install devkitPro (includes devkitARM and libnds)
  2. Download the source code (main.c + Makefile)
  3. Run make in the project directory
  4. Output: program.nds (~100 KB ROM file)

Running on real hardware

You need a flashcart (e.g. R4, DSTT, Acekard) with a microSD card:

  1. Copy program.nds to the microSD card
  2. Insert the microSD into the flashcart
  3. Insert the flashcart into your DS
  4. 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


Source (ds-game-engine.zip)


Compiled ROM (ds-game-engine.nds)



Discussion


Feel free to ask or discuss in this Reddit thread