A native .3dsx homebrew text-completion app for the New Nintendo 2DS XL /
New 3DS family. It runs the 260K-parameter TinyStories model entirely on the
console with a portable C FP32 engine adapted from
llama2.c. There is no network
inference, response table, or fake streaming.
This is a TinyStories completion model, not an instruction-following assistant. Start with Once upon a time. Quality is intentionally tiny.
- Download
tinychat3ds-sdcard.zipfrom the latest release. - Unzip it and copy the
3dsfolder to the root of your SD card, so you end up withSD:/3ds/tinychat/containingtinychat3ds.3dsx,tinychat3ds.smdh,stories260K.bin, andtok512.bin. - Put the SD card back in the console and open the Homebrew Launcher.
- Pick TinyChat 3DS.
The model and tokenizer must sit next to the .3dsx in that exact folder — the
app loads them from sdmc:/3ds/tinychat/. The release also ships a bare
tinychat3ds.3dsx for people who already have the two data files in place.
Controls: tap the custom bottom-screen keyboard, then Send (or A). Tap
Stop (or B) to cancel and Clear to reset. D-pad Up/Down scrolls the
top-screen transcript; START exits.
./scripts/launch-azahar.shAzahar opens the packaged .3dsx from an isolated profile under
tools/azahar-profile/user. If macOS blocks first launch, right-click
tools/Azahar.app, choose Open once, then rerun the command.
Requirements on an Apple Silicon Mac are Docker Desktop, the Xcode command line
tools, and standard curl/make. No administrator access is needed.
./scripts/setup-macos.sh # pinned official model, devkitPro image, Azahar
make desktop # build portable host CLI
make test # compare fixed greedy output with pinned upstream
make asan # host AddressSanitizer + UndefinedBehaviorSanitizer
make package # build .3dsx and ready-to-copy SD folder
./scripts/benchmark.sh # desktop-labelled timing onlyNative devkitPro installations are also supported: set DEVKITARM, then
./scripts/build-3ds.sh uses it. Otherwise the script uses devkitPro's
official, digest-pinned devkitarm container while Azahar remains native on
the host.
Outputs:
3ds/tinychat3ds.3dsx— native homebrew executabledist/sd-card/3ds/tinychat/— copy directly into an SD card rootbuild/tinychat-cli— host verifier sharing the exact inference core
The checkpoint is read into one resident allocation; execution does not use
POSIX mmap, OpenMP, NEON, ARM64, or GPU compute. The model uses a 128-token
runtime KV cache and at most 48 new tokens per submission. State and cache are
rebuilt on each Send, so each prompt is an independent completion even though
the presentation is chat-like. All activation, cache, and sampler buffers are
preallocated; there is no allocation per generated token.
The 3DS app calls libctru's documented osSetSpeedupEnable(true) for
New-family hardware and uses one worker with the launcher's default affinity
(-2). It does not request extra system-core time through
APT_SetAppCpuTimeLimit, so it does not assume all New 3DS physical cores are
available. The UI displays its tracked heap and libctru's live application-region
free memory. libctru's
OS API
and thread API
are the authoritative references.
Errors for missing/truncated/wrong-vocabulary files, invalid checkpoints, allocation failure, empty prompts, cancellation, EOS/BOS, and context limits are surfaced on screen. In particular, a tokenizer with entries beyond the checkpoint's exact 512 vocabulary is rejected, preventing accidental use of llama2.c's usual 32K tokenizer.
- Desktop (Apple M3, macOS 26.5): fixed greedy output is byte-identical to pinned upstream llama2.c; failure-path tests and sanitizers pass.
- 3DS cross-build: verified as an ARMv6K 3DSX using devkitARM r68-1, libctru 2.7.0-1, citro2d 1.7.0-1, and citro3d 1.7.1-2.
- Azahar: packaged into an isolated virtual SD and launched with the official
Apple Silicon 2126.0 release. The screenshots above are from that run. See
docs/VERIFICATION.mdfor observed GUI results and timings. - Real New 2DS XL: not tested. Emulator and desktop speed do not validate the eventual 20 token/s hardware target. Quantization and parallel kernels are intentionally deferred until this FP32 baseline is measured on hardware.
Exact revisions and SHA-256 hashes are in DEPENDENCIES.lock; third-party
notices are in THIRD_PARTY.md.
MIT — see LICENSE. The bundled stories260K checkpoint and tok512.bin
tokenizer come from karpathy/tinyllamas
and are MIT-licensed; see THIRD_PARTY.md.

