Nintendo DS code editor and scriptable game engine

2026-01-3118:2719050crl.io

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…

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.

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.


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. 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
}

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.

  • 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)

  • 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!)
  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)

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.

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). Ensure JavaScript is enabled and the page has finished loading. Reload browser page if it doesn't work.


Source (ds-game-engine.zip)


Compiled ROM (ds-game-engine.nds)


Feel free to ask or discuss in this Reddit thread


Read the original article

Comments

  • By functionmouse 2026-01-3122:38

    Yesssss this is right up my alley with my current projects. I'm working through K&R 2nd Ed and making little demo apps with libnds using framebuffer manipulation for graphics along the way. My goal is to eventually end up with a useful 15bit sprite-ing application, and an endless scrolling handwriting journal thingy with bookmarks. Thank You for posting this! I'm loading this onto my DSi XL right now.

  • By liveoneggs 2026-01-3122:115 reply

    The Nintendo DS (lite or XL) is probably the greatest portable gaming system to ever exist.

    • By PacificSpecific 2026-02-015:22

      Most definitely.

      Less popular opinion : the ps vita was also one of the greatest despite Sony's horrid support and faith in the product.

    • By herpdyderp 2026-02-0114:284 reply

      Over the Steam Deck even?

      • By Wowfunhappy 2026-02-0117:081 reply

        Here's the problem with the Steam Deck, and every modern handheld. (And actually also the DSi XL noted in GP.)

        The DS Lite can fit in your pocket. Comfortably. Not, like, technically fit, at the cost of making it uncomfortable to walk around. You can keep a DS Lite in your pocket all day, ready to be pulled out the moment you enter the crowded subway, or while in the doctor's office. Situations you didn't necessarily plan for.

        The stupid Switch, and especially the even larger Steam Deck, I never actually have with me when I want it. All I have is my stupid iPhone, which is great for many things but doesn't have buttons, and it turns out you kind of need those for games. Developers have been trying, for nearly two decades now, to find ways to avoid buttons, and it even kind of works sometimes, but not really. Not when you want precise control over a character.

        No one makes portable consoles anymore. I mean, they're portable in the way that a laptop is portable. They're handheld in the way a vacuum cleaner is handheld. But they're not portable like the DS Lite or GBA SP.

        You can buy these little Android things designed for emulators, and I've almost bought them a few times, but I've pretty much already played all the NES games I want to play, and because there are very few new NES games being released, the library doesn't expand.

        • By ctippett 2026-02-021:511 reply

          I gifted a Brick Hammer[1] to my brother-in-law for Christmas. It's an amazing little device, form factor of a Gameboy Pocket and can run games from the PS1/N64 down to NES. The build quality is extremely high too (I got him the metal chassis version). I think it can even run 3DS games, but don't quote me on that.

          [1]: https://retrohandhelds.gg/trimui-brick-hammer-review/ (most links are to third-party resellers, so a link to a review is probably safest)

          • By Wowfunhappy 2026-02-023:00

            Thanks, but same problem. I meant "NES" as a stand-in for "retro games".

            Now, what I would almost certainly buy is a Steam Deck in that size. It doesn't need to be as powerful as a Steam Deck (impossible, of course), just enough to play 2D indie games.

      • By pjmlp 2026-02-0215:001 reply

        Yes, that is mostly for portable PC games, and lacks what using PSP, PSP Vita, Game Gear, and all others have in convenience, battery, and game library, on games designed on purposed for tiny screens.

        Many people talk about the Steam Deck, when in reality it isn't that much of a hit in units sold compared with any portable games console from Nintendo, SEGA, Sony.

        • By Wowfunhappy 2026-02-0514:15

          Well Switch games aren't really designed for small screens either.

          I haven't bought a Steam Deck because I think it's (1) too big (see my sibling comment on console size—to be fair I own a Switch 1 and 2, but the Steam Deck is even bulkier) and (2) too fiddly, this is the thing I've never liked about PC gaming.

      • By LoveMortuus 2026-02-0215:13

        For me the main issue with the Steam Deck and many other such devices is the battery life. I can play Nintendo DS for multiple days, whereas I have to charge every day or just keep the Steam Deck plugged into the charger.

      • By olivierestsage 2026-02-0216:59

        Steam Deck is the device of my wildest childhood/teenage dreams. A fully functional portable Linux computer that also plays PC games, are you kidding me?! But I do get how it's not really in the same category of old school handhelds. I don't take it around much except within the house.

    • By haunter 2026-01-3123:234 reply

      Honestly I'd say that's the New 3DS XL.

      • By komali2 2026-02-012:172 reply

        Agreed over the dsi, though I love it. The only exception is if you only play physical media, then the DS lite's ability to play the entire GBA library is pretty cool.

        For me it's the fact that I can emulate basically all retro consoles on the n3ds, including imo the best existent way to play VirtualBoy games in actual stereoscopic 3d. That on top of the fact that it's the only way to play 3ds games in the native experience, with 3d, which is impossible on any emulator. Combined with the social features and build quality of the device, and it's unbeatable. Get a USB 3ds charger and it's the perfect travel console. I bring it on every trip and leave it on streetpass mode. Especially for Japan trips, I still to this day pick up new street pass pings, which is so incredibly delightful to find when I get back to the hotel room.

        Though I prefer the non xl version, as it's a truly portable console rather than the XL which is huge and heavy in my bag.

        • By gyomu 2026-02-018:24

          > the only way to play 3ds games in the native experience, with 3d, which is impossible on any emulator

          You can get that with a headset; there's emulators for the Quest, or you can use Citra in SBS mode with a headset like the Vision Pro or Xreal glasses. But yeah it's more finnicky for sure.

        • By holly01 2026-02-017:54

          Fun fact, all 3ds models can actually play GBA games natively. Nintendo only ever offered GBA games to early adopters who paid the launch price for the original 3ds after a significant price cut, but it's possible to sideload any rom to run natively.

      • By regenschutz 2026-02-011:382 reply

        I'm not sure. At least for me, part of the charm of these older consoles (do they qualify for the "retro" term yet?) is the smaller resolution of the displays. Especially for the bottom screen on the DS line, there is something very warm, fuzzy, and cosy about clearly being able to see each individual pixel.

        Perhaps it's just nostalgia. I still own my DS Lite, though.

        • By soulofmischief 2026-02-0110:17

          The DS came out ~21 years ago, in 2004.

          In 2004, the games I played that had come out ~22 years prior, give or take two years, were already considered retro: Donkey Kong, Pitfall, Pac-Man, Asteroids, etc. And ~21 years prior was the great video came crash of '83.

          Suffice to say, we're dinosaurs. I've made my piece with it and spend a considerable amount of time listening to old British dudes break down 30 year old games.

        • By someperson 2026-02-011:57

          The best music is always from ones own youth

      • By hoherd 2026-02-0413:48

        I carried my N3DSXL pretty much everywhere while living and working in SF. Streetpass was such a cool feature for the urban lifestyle. The 3D experience on the New model was so much better than the old ones.

      • By liveoneggs 2026-02-013:542 reply

        I only played it briefly with a friend (street fighter) and the actual 3d seemed neat but it didn't wow me (or my eyes couldn't focus properly?).

        I wanted to be wow-ed at the level of that old cowboy arcade hologram game. :)

        I'm sure since it was the newest hardware it was the best.

        • By Wowfunhappy 2026-02-0117:11

          The biggest reason the 3DS can't provide what feels like a hologram is that the screen is too small. As objects come closer, they need to get bigger, but they can't get bigger than the screen's dimensions. So most games focused on objects going into the screen, instead of popping out.

          But I think the 3D served one really wonderful purpose: it made the small screen feel larger overall. Not large, maybe, but larger than it really was.

        • By tastyfreeze 2026-02-014:51

          > old cowboy arcade hologram game

          Sega Time Traveler

          Playe it a few times. Never made it very far. Sure was cool to look at though.

    • By mghackerlady 2026-02-022:261 reply

      Gaming system? yes, but the PSP is what I'd carry around today for its amazing multimedia capabilities

      • By MisterTea 2026-02-0215:481 reply

        My friend cracked his PSP and had a wild setup for its time with all the console emulators, ports of PC games like Doom, Quake, Hexen 2, and a media player for music and video. Neat system for its time.

        • By mghackerlady 2026-02-0314:51

          It's still great today. The thing can play (some) N64 games, and someone got an (albeit very bad) DS emulator running on it

    • By mooglevich 2026-02-0315:05

      Haha I don't have anything more insightful to say other than a: hell yeah.

  • By cmovq 2026-01-3120:551 reply

    The scripting language looks like it would be more cumbersome to use than C.

    • By RodgerTheGreat 2026-01-3121:034 reply

      I did a double-take at

          > Executes one line of script per frame (~60 lines/sec).
      
      Makes the "runs at 60FPS" aspect of the engine feel a lot less relevant. At this speed, anything more complex than Pong would be a struggle. Even a CHIP-8 interpreter is usually expected handle a dozen or so comparably expressive instructions per frame.

      • By famahar 2026-02-011:20

        Which is why I love this. Extreme constraints. Takes a lot of creativity to make something interesting, without feeling overwhelmed with possibility. I'm considering making tiny arthouse game projects with this.

      • By nxobject 2026-01-3122:39

        Don’t worry, you can’t have that many lines anyway…

        > Up to 128 script lines per program

      • By networked 2026-02-0112:57

        I agree. It seems like interpreting one instruction per frame is the developer's way to guarantee real-time performance. I don't want to discourage the developer from experimenting with this design. What I think they should do is determine the most instructions they can interpret each frame.

        I like the idea of on-device programming like this. While I haven't used it, I know there is a DSiWare application Petit Computer that lets you implement more complex (sprite-based) games in a Basic dialect: https://en.wikipedia.org/wiki/Petit_Computer. As DSiWare, it has an official video trailer: https://www.youtube.com/watch?v=4AYlEo3rJHs.

      • By azharav 2026-02-0111:39

        ،sh12

HackerNews