Back

Switch React Menu

A Minimalist Home Menu for Nintendo Switch - December 2024

Switch React Menu

Role

  • Mobile Development
  • UI + UX Design

Team

  • Solo

Timeline

  • 1 Month

Overview

This Nintendo Switch App Launcher is a minimalist implementation of a traditional home menu. It relies on the JavaScript library nx.js, built by TooTallNate, to render React components to a generic canvas element through the react-tela library.


While the react-tela framework itself is runtime-agnostic, it was the only way I could find to run the JSX runtime on the Nintendo Switch. Even though this interface relies on minimal reactivity, I thought the concept of running a React app on a console was interesting enough to be worth exploring.

Context

In the Winter of 2024, I found myself with a full break from school for the holidays and a lot of time to kill. It made sense to start working on something new that I could show off, while also keeping the scope small enough that I could realistically finish the thing by the end of the break.


I had already been pretty invested in the world of Nintendo homebrew by this point. I had custom firmware running on not just my switch, but pretty much every Nintendo console I owned (Wii, DSI, 3DS, and Wii U). So I really liked the idea of taking a stab at building an app for a Nintendo console. However, I found the typical options to be a little lacking. Working with C/C++ seemed like it would be too much of a pain to get started, and I didn't want to spend too much time tinkering with the low-level details of the console's APIs in order to build a simple app.


While JavaScript may not be an obvious choice, it's actually relatively fast compared to other high level options like Python, and its frontend ecosystem plays well with UI development.


Ultimately, nx.js ended up being amazing for a couple of reasons:

Low-Level Control

The framework gave me shockingly low-level access to the hardware. Through the Canvas API, I was able to draw pixels and shapes directly to the screen instead of interacting with a Document Object Model (DOM), like in traditional JavaScript.

TypeScript Support

Coming from a Java background, I'm a big fan of what TypeScript brings to JavaScript. Adding type safety to code is a common sense addition to any language, and it's become so ingrained in my workflow that I can't really live without it.

React Tooling

React's declarative UI feels like a natural fit for a quick and dirty app like this. Reacting to state changes felt far more natural to work with than manually updating the UI, especially when it came to handling controller input.


Development

The development process was a bit of a rollercoaster. I started out by trying to use the Canvas API directly, but it was a bit of a pain to work with. I ended up using the react-tela library to render the React components to a canvas, which was a lot easier to work with.


I also had to become familiar with the Switch.Application API. It reads the switch's app titles, authors, and box art from the Switch's app catalog. It can be used to iterate over the app catalog and sort for box art like this:

javascript
const switchApps = Array.from(Switch.Application).filter(
  (app) => app.icon
);

When fetched, images are stored as bitmap files in order to render them to the canvas. If images can't be fetched, the loop continues to the next app.

javascript
for (const app of switchApps) {
  let img;
  if (app.icon) {
    img = await createImageBitmap(new Blob([app.icon]));
  }
  if (!img) continue;
  //...
}

Each image renders exactly 40 pixels away from each other so that the grid can fit exactly 4 apps across the Switch's 1280 pixel width screen. It offers future proofing in case Nintendo decides to release a new model with a different screen size at some point in the future (though I highly doubt it, given the existence of the Switch 2)


Controller inputs are handled by a custom hook that listens for button presses and updates the UI accordingly. It listens for both left and right directional inputs on the stick and D-pad, as well as detecting shoulder buttons to quickly swap pages. It also listens for the A button to launch whatever app is selected.


Since drawing borders around images isn't directly supported by the Canvas API, I had to use a bit of a tricky workaround. I drew a slightly larger Rectangle object directly behind the image to create the border. It uses a 5-pixel white stroke to create the border effect whenever React detects that the image is selected.

javascript
{displayedApp.app.icon && (
  <>
    {isSelected && (
      <Rect
        x={displayedApp.x - 5}
        y={displayedApp.y - 5}
        width={displayedApp.width + 10}
        height={displayedApp.height + 10}
        fill="none"
        stroke="white" // Draws the border
        lineWidth={5} // 5-pixel width
      />
      //...
)}

While the current implementation provides a solid foundation for browsing and launching apps, there's still plenty of room for growth. Features like filtering apps by name or developer, photo album support, and other improvements would make the experience feel more complete. These aren't too complicated to implement, and the React-forward architecture will make it pretty straightforward to add these things in the future if time permits.