Skip to content

natureglass/switch-webapp-player

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

switch-webapp-player

switch-webapp-player is an nx.js homebrew app for running one fullscreen static Canvas/WebGL/WASM-style web app from the Switch SD card. It is based on the nx-canvas template and uses nx.js runtime APIs directly. It does not use the Switch WebApplet/browser.

Folder Layout

The MVP hardcodes this app root:

sdmc:/switch/webapps/
  default/
    app.json
    bundle.js
    asset.json
    app.wasm
    assets/
    shaders/
  wasm-test/
    app.json
    bundle.js
    app.wasm

Example app.json:

{
  "title": "Default Web App",
  "entry": "bundle.js",
  "width": 1280,
  "height": 720,
  "fullscreen": true
}

The runner scans sdmc:/switch/webapps/ for child folders containing app.json. If the only app is default/, it launches that app immediately and skips the launcher screen. If multiple apps are present, it shows a simple Canvas launcher.

Launcher controls:

  • Up/Down or ArrowUp/ArrowDown: select app
  • A or Enter/Space: launch selected app
  • B or Escape: refresh/rescan

Runtime combo:

  • Hold L + R + Minus for about one second to stop the current app.
  • If multiple apps are available, this returns to the launcher.
  • If only default/ exists, this exits the app.

The selected entry file is loaded from manifest.entry. Repeatable sample apps are included in examples/; copy one example's files to sdmc:/switch/webapps/default/ for direct-launch testing, or copy several examples into separate subfolders to use the launcher.

For Citron on Windows, the matching folder is usually:

C:\Users\<you>\AppData\Roaming\citron\sdmc\switch\webapps\default\

Available examples:

  • examples/default-webapp/ - immediate Canvas 2D draw, requestAnimationFrame() animation, and fetch("./asset.json").
  • examples/fetch-test-webapp/ - checks fetch("asset.json"), fetch("./asset.json"), fetch("/asset.json"), nested asset fetches, arrayBuffer(), and 404 handling.
  • examples/wasm-webapp/ - loads ./app.wasm with fetch().arrayBuffer(), calls WebAssembly.instantiate(), and draws the result.
  • examples/gamepad-webapp/ - checks navigator.getGamepads(), gamepad connection events, axes, buttons, and D-pad movement.
  • examples/pointer-webapp/ - checks touchscreen-to-pointer mapping, mouse fallback events, mirrored window touch events, coordinates, and drag trails.
  • examples/webgl-probe-webapp/ - checks canvas.getContext("webgl") / experimental-webgl and shows native WebGL diagnostics.
  • examples/webgl-egl-probe-webapp/ - checks the nonstandard EGL/OpenGL ES backend prototype diagnostics and optional GPU clear probe.
  • examples/webgl-clear-webapp/ - first native WebGL milestone target; animates clearColor() once the patched nx.js runtime exposes WebGL.
  • examples/webgl-state-webapp/ - checks basic WebGL state calls and getParameter().
  • examples/webgl-shader-state-webapp/ - checks shader/program lifecycle state.
  • examples/webgl-buffer-state-webapp/ - checks buffer upload and vertex attribute setup.
  • examples/webgl-triangle-webapp/ - draws a software-rasterized triangle through drawArrays().
  • examples/webgl-transform-triangle-webapp/ - animates a triangle with transform/color uniforms.
  • examples/webgl-quad-webapp/ - draws an indexed quad with drawElements().
  • examples/webgl-textured-quad-webapp/ - uploads generated RGBA pixels and draws a textured quad.
  • examples/webgl-external-texture-quad-webapp/ - loads ./assets/checker.rgba with fetch().arrayBuffer(), uploads it with texImage2D(), and draws a textured quad.
  • examples/webgl-sprite-mesh-benchmark-webapp/ - cycles 18/36/72 rotating alpha sprites in one dynamic indexed mesh.
  • examples/webgl-axis-aligned-sprite-benchmark-webapp/ - cycles 18/36/72 non-rotated alpha sprites to exercise the native indexed-quad fast path.
  • examples/webgl-low-trig-axis-sprite-benchmark-webapp/ - cycles 18/36/72 moving non-rotated alpha sprites with per-frame mesh upload but without per-sprite trig.
  • examples/webgl-uniform-axis-sprite-benchmark-webapp/ - cycles 18/36/72 moving non-rotated alpha sprites using a static quad, uniform2f() offsets, and repeated drawElements().
  • examples/webgl-static-axis-sprite-benchmark-webapp/ - cycles 18/36/72 static non-rotated alpha sprites with no per-frame mesh upload, isolating native draw cost.

Raw RGBA texture assets are currently width * height * 4 bytes in RGBA8 order. The external texture example uses:

const bytes = new Uint8Array(await fetch("./assets/checker.rgba").then((r) => r.arrayBuffer()));
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 64, 64, 0, gl.RGBA, gl.UNSIGNED_BYTE, bytes);

Example PowerShell copy command for Citron:

Copy-Item -Recurse -Force .\examples\wasm-webapp\* "$env:APPDATA\citron\sdmc\switch\webapps\default\"

Example multi-app launcher layout for Citron:

New-Item -ItemType Directory -Force "$env:APPDATA\citron\sdmc\switch\webapps\wasm-test"
Copy-Item -Recurse -Force .\examples\wasm-webapp\* "$env:APPDATA\citron\sdmc\switch\webapps\wasm-test\"
New-Item -ItemType Directory -Force "$env:APPDATA\citron\sdmc\switch\webapps\gamepad-test"
Copy-Item -Recurse -Force .\examples\gamepad-webapp\* "$env:APPDATA\citron\sdmc\switch\webapps\gamepad-test\"

Runtime Shims

The app installs browser-like globals before loading the bundle:

  • window, self, and global point at globalThis
  • window.innerWidth, window.innerHeight, and window.devicePixelRatio
  • existing nx.js performance.now() and event APIs
  • timer-backed requestAnimationFrame() / cancelAnimationFrame() shims for the current MVP
  • browser-style navigator.getGamepads() wrapper with standard-ish A/B/X/Y ordering and gamepadconnected / gamepaddisconnected events
  • touchscreen forwarding from native screen touch events to pointerdown / pointermove / pointerup, mouse fallback events, and mirrored window / document touch events
  • WebGL context probing for webgl and experimental-webgl; with the patched local nx.js runtime this exposes an experimental framebuffer-backed WebGL subset
  • a minimal document shim
  • one fullscreen canvas returned by document.createElement("canvas") and document.getElementById("canvas")
  • app-root-relative fetch() for local SD-card files

Local fetches such as fetch("./asset.json"), fetch("asset.json"), fetch("/asset.json"), fetch("./app.wasm"), fetch("./assets/texture.png"), and fetch("./assets/checker.rgba") resolve against the selected app root.

WASM bytes can be loaded with:

const bytes = await fetch("./app.wasm").then((r) => r.arrayBuffer());
const result = await WebAssembly.instantiate(bytes, imports);

Current Limitations

  • No real DOM.
  • No HTML parser.
  • No CSS/layout.
  • WebGL is an experimental framebuffer-backed subset, not a full browser/GPU WebGL implementation.
  • WebGL currently supports a narrow 2D-oriented path: clear/state calls, shaders/program handles, buffers, simple uniforms, drawArrays(), drawElements(), RGBA TEXTURE_2D uploads, and nearest/linear/clamp/repeat texture parameters.
  • WebGL does not yet support real GLSL execution, varying evaluation beyond the built-in UV path, depth testing, blending, framebuffer objects, mipmaps, compressed textures, cube maps, or GPU acceleration.
  • No Web Workers.
  • No Service Workers.
  • No IndexedDB.
  • localStorage is only available if provided by nx.js.
  • Only one fullscreen canvas is supported.
  • JS should be bundled into a single script-style bundle.js for now.
  • Static ESM import / export syntax in bundle.js is not supported yet. Use a bundler output format such as IIFE/plain script.
  • Gamepad mapping is currently minimal. Buttons are exposed as A, B, X, Y, L, R, ZL, ZR, Minus, Plus, StickL, StickR, Up, Down, Left, Right.
  • Pointer mapping is currently touch-only. It emits one pointer stream per changed touch and does not yet synthesize hover, wheel, pointer capture, or CSS cursor behavior.

See docs/webgl-plan.md for the native-backed WebGL milestone plan.

Next Steps

  1. Record Citron/device FPS for rotating and axis-aligned WebGL sprite benchmarks at 18, 36, and 72 alpha sprites.
  2. Add optional audio support.
  3. Add an optional lightweight index.html loader later.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors