Rust 🦀 on the 3DS

Originally posted on 3/30/2025 on my old blog at https://baileytownsend.dev/articles/rust-on-the-3ds

I Recently bought a 3DS, so of course I had to see if there was a way to write Rust for it, and thanks to ctru-rs , you can. The 3DS has a large library of games to play. It has the whole 3DS library, while also being able to natively run DS, GBA, and Game Boy titles making it in my opinion making it the best daily handheld gaming device.

How does it work?

Thanks to the work done by the @rust3ds organization you can write rust for the armv6k-nintendo-3ds target. A lot of the apis for it are implemented by linking to a libc(devkitARM) library that is maintained by devkitPro . So a lot of what the ctru-rs/rust3ds project does is gives you, the end user, safe wrappers around the C libraries to make software for the 3DS safely in rust.

A lot of terms and organizations so I wanted to make a list to break it down. I am going to start with the C libraries.

devkitPro - The organization that provides the C libraries for the 3DS, along with other gaming consoles.

libctru - C library for writing code for the 3DS

citro3d - C library providing easy apis for displaying 3D graphics

citro2d - C library build on top of citro3d to display 2d graphics. Text, sprites, and 2d shapes.

Then for the rust sie of things.

rust3ds - The organization that maintains support for the armv6k-nintendo-3ds target and safe rust bindings to the devkitPro C libraries as well as crates to safely call them in a rusty way.

ctru-sys - Direct rust bindings to the libctru C library, unsafe.

ctru-rs - A Safe and idiomatic Rust wrapper around the libctru library. This is the library you call when writing Rust software on the 3DS. It handles all the unsafe implementations for you.

cargo 3ds - cargo commands to work with 3DS projects. Example, you use cargo 3ds build and cargo 3ds run to build and run the project.

citro3d-sys - Direct rust bindings to the citro3d C library.

citro3d-rs - A work in progress crate for safe rust bindings to the citro3d C Library, this is the crate you call to use 3D graphics in your 3DS software.

citro2d-sys and citro2d - More on this under 2d graphics

Setup

The rust3ds team has an excellent Getting Started guide for setting up a dev environment to get started on writing rust on the 3DS. I am going to brush over a lot of the same information, but mostly focus on the pain points I had when I was getting started. This guide assumes you have rust installed and familiar with setting up and running projects with it.

Installing and setting up the devkitARM toolchain

Before you can build any of these projects you will need to install and set up the devkitARM toolchain by following their Getting started guide .

export DEVKITPRO=/opt/devkitpro
export DEVKITARM=${DEVKITPRO}/devkitARM
export DEVKITPPC=${DEVKITPRO}/devkitPPC

export PATH=${DEVKITPRO}/tools/bin:$PATH
export PATH=${DEVKITARM}/bin:$PATH

If you get a error: linker arm-none-eabi-gcc not found it is most likely because of bullet point 4. So need to check those and make sure it is accessible. Some more context .

cargo-3ds

cargo 3ds is a command tool you use to interact with your 3DS projects. You have to use this to build and run projects. Normal cargo run and cargo build does not work. You can install it by cargo install --locked cargo-3ds.

Creating a project

To create a new project you can use cargo 3ds new, this will create a new project that you can build and run. When I first started using it I had the same problem as listed in issue #68 when using cargo 3ds new, but since then it has been resolved. If you have a similar problem may use cargo install --locked --git https://github.com/rust3ds/cargo-3ds instead to make sure you have the latest version with the patch.

Or for some extra bell and whistles you can use my template I have created for trying out my work on citro2d. I'll go a bit more into what those extras are in My development workflow .

Where do I even run this?

Once you build your project with cargo 3ds build you should see output that looks a bit like this

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
Building smdh: /home/baileytownsend/Documents/code/rust/games/citro2d-project-template/target/armv6k-nintendo-3ds/debug/citro2d-project-template.smdh
Building 3dsx: /home/baileytownsend/Documents/code/rust/games/citro2d-project-template/target/armv6k-nintendo-3ds/debug/citro2d-project-template.3dsx
Adding RomFS from /home/baileytownsend/Documents/code/rust/games/citro2d-project-template/romfs

The file ending in .3dsx is what you want. This is what you install on the 3DS or the file you open inside the emulator. This is important mostly for when using a emulator.

Emulators

During development, I recommend using an emulator I found it is a bit of a faster feedback loop. But which one? You may have heard, there has been a bit of a controversy with 3DS emulators. Because of this the projects have been a bit splintered across forks, but I am going to list some I've had success with.

Citra (rip)

Lime3DS (archived)

Azahar Emulator (Successor to lime-3ds)

Panda3DS (2D complex shapes do not show as well for some reason)

All of these also allow you to open the .3dsx as the first argument after executable's name. For example in my projects I usually have a just file that builds, and then launches it in an emulator.

shapes-example:
cargo 3ds build
lime3ds target/armv6k-nintendo-3ds/debug/citro2d-project-template.3dsx

Real hardware

One of the coolest parts of writing software for these older game systems is being able to run it on the actual hardware. I will never forget how awesome it was to the see the GBA load screen on real hardware before booting into a game I had made.

Thanks to homebrewing it is easy to get what you write onto your 3DS.

What you will need.

Any 3Ds or 2Ds model

Follow the 3DS Hacks Guide to install a custom firmware on your 3DS. It does not matter which model, or version it has, the guide has a way for all of them.

Once you have your 3DS loaded with the custom firmware and the homebrew launcher setup. Open the homebew launcher and on the main screen of the homebrew launcher press Y on your console. You should see the following screen below.

After that it is as easy as using cargo 3ds run to send the program over to the 3DS! If you have issues with it not being able to find the device make sure your 3DS is on the same network and can use this command to specify the ip address found on the 3dslink NetLoader screen cargo 3ds run --address xxx.xxx.xxx.xxx

My development workflow

I did not have luck with using rust analyzer as setup here with my 3DS projects. But I found using bacon and bacon-ls helped a ton to keep a familiar workflow. I am going to use my citro2d-project-template template as the example for this.

Although not strictly needed. I found having a config.toml setup helpful, especially for bacon-ls. Just make sure to update with the locations of your env variables for DEVKITARM

[jobs.bacon-ls]
command = [
"cargo",
"3ds",
"check",
"--message-format",
"json-diagnostic-rendered-ansi",
]
analyzer = "cargo_json"
need_stdout = true

[exports.cargo-json-spans]
auto = true
exporter = "analyzer"
line_format = "{diagnostic.level}|:|{span.file_name}|:|{span.line_start}|:|{span.line_end}|:|{span.column_start}|:|{span.column_end}|:|{diagnostic.message}|:|{diagnostic.rendered}|:|{span.suggested_replacement}"
path = ".bacon-locations"

Then that is it! You should now be seeing the output of cargo 3ds check in your editor to help you write your 3ds programs.

2d Graphics

Since the 3DS is a gaming console I really wanted to try out making some simple games on it. Ctru-sys has a ton of great examples to get started and to write some great homebrew apps. But I did not find any to easily make games. I had found that they have started an implementation of citro3d for 3d graphics and they have a really cool example app that shows a triangle in 3D, but I'll be honest 3d graphics is a bit over my head and I wanted something simpler to start with.

There is a C library called citro2d that is built on top of citro3d that has apis for rendering shapes, loading in custom fonts, and even sprite sheets. Perfect! Just where I wanted to start, expect for one thing. Citro3d-rs had been started, but since the C library builds on top of the 3d one the work on the 2d rust implementation had not been started. So I decided to take a look at it and see if I could get the bare minimal going. And I am happy to say I have had some success!

The citro 2d_shapes example in rust found here

A simple implementation of the classic game breakout in rust found here

Right now it can only render the 2D shapes on the top and bottom screen. But I have plans to add the rest of the features citro2d has, like custom fonts and sprites. If you're interested in the status of that can follow along on this draft PR .

Closing

That's pretty much it! Thanks for checking out my post about using Rust on the 3DS, and thank you to the work done by @rust3ds who without them none of this would be possible!