Skip to content

Nintendo Switch

Olivier Biot edited this page Nov 13, 2026 · 2 revisions

Nintendo Switch (nx.js)

nx.js runs JavaScript homebrew on the Nintendo Switch. It implements the web APIs a game uses (Canvas 2D, WebGL 2, fetch, createImageBitmap, Web Audio, gamepads, touch) but has no web page around them: no document, no HTML elements, no CSS.

melonJS runs there from melonJS 20.14. Support is experimental.

Starting the game

There is no page to create a canvas in, so give the application the one to draw to. On nx.js that is the global screen, the Switch's 1280 × 720 display:

import { Application, video } from "melonjs";

const app = new Application(1280, 720, {
    canvas: screen,          // nx.js's display
    renderer: video.AUTO,    // WebGL 2 where available, otherwise Canvas
    scale: 1,
});
await app.init();

Set up the nx.js project itself as its documentation describes.

What's different without a page

  • Size: design the game at the display's size. There is no page to fit, so scale: "auto" has nothing to measure, and the game is drawn at the scale it is given. The console scales the picture itself when docked.

  • No page features: app.parentElement is undefined, and fullscreen and pointer lock do nothing.

  • Assets load through fetch and createImageBitmap, as in a browser, and a relative path is read from the app's RomFS. Two exceptions:

    • Tiled maps must be JSON (.tmj and .tsj): XML needs DOMParser, which nx.js doesn't have.
    • The video and script asset types need a page, and fail.
  • Input: touches on the screen reach the game as pointer events, as usual. There is no mouse. The Joy-Con are standard gamepads, and keyboard events only come from a USB keyboard.

  • Sound needs no tap or click to start. nx.js can't say which formats it decodes, so melonJS uses the first extension given to audio.init(): name one it decodes first, such as audio.init("ogg,mp3"). Plain playback, volume and stereo panning work, but nx.js's Web Audio has no PannerNode, OscillatorNode or BiquadFilterNode, so 3D positional sound, audio.tone() / audio.noise() and the reverb, echo and compressor effects throw.

  • Fonts: nx.js's FontFace only takes the font's bytes, so a fontface asset (a URL) fails. Load the bytes yourself, and add the face before a Text uses it. It is ready once made; don't call its load(), which throws on nx.js:

    const bytes = await (await fetch("font.ttf")).arrayBuffer();
    fonts.add(new FontFace("Pixel", bytes));
  • Saves: save uses localStorage as usual. If the app declares user save data (userAccountSaveDataSize in its NACP), the first time melonJS reads localStorage is when the Switch asks the player to pick a user. An app that saves nothing should leave that size at 0, so no picker appears.

See also

Clone this wiki locally