# HHL Gamepad Config > HHL Gamepad Config (internal name hoja3) is Hand Held Legend’s free, offline-first web app (PWA) for configuring game controllers that run HOJA firmware, e.g. GC Ultimate, ProGCC and Super Gamepad+. It connects over USB with WebUSB, updates firmware, and is fully deep-linkable so assistants can send customers straight to the right page or a pre-filled set of settings. App URL: https://handheldlegend.github.io/hoja3/ Key facts: - Works in Chromium browsers (Chrome, Edge, Opera, Brave…) on desktop and Android, served over https. Not on iPhone/iPad (no WebUSB); the demo and Arena still work there. - Connect with a USB **data** cable. Only **Switch** and **Steam (SInput)** output modes talk to the app; if a controller starts in another mode, unplug it and hold **A** or **B** (East or South) while plugging it back in. - Every change applies live; **Save** writes it to the controller’s flash so it survives unplugging. - Try it without hardware: https://handheldlegend.github.io/hoja3/?demo (a simulated controller). - Routing is hash-based: `https://handheldlegend.github.io/hoja3/#/?=`. ## Deep links - Open a page: `https://handheldlegend.github.io/hoja3/#/[?param=value&…]`, e.g. `https://handheldlegend.github.io/hoja3/#/whats-new?changes=input`, `https://handheldlegend.github.io/hoja3/#/input?mode=switch&tab=remap`, `https://handheldlegend.github.io/hoja3/#/joysticks?stick=left&tab=calibrate` - Propose settings: `https://handheldlegend.github.io/hoja3/#/apply?=&…&then=`. Keys come from the catalog below; values are validated (numbers are clamped to the step, enums accept the value, label or an alias, booleans accept on/off/true/false/1/0, colors are #rrggbb). `then` picks the page shown afterwards. The user sees a confirmation dialog before anything is written. - Example (Set haptics.intensity to 50% and haptics.triggerFeedback on): https://handheldlegend.github.io/hoja3/#/apply?haptics.intensity=50&haptics.triggerFeedback=on&then=haptics&source=assistant - Example (Set rgb.mode to Static): https://handheldlegend.github.io/hoja3/#/apply?rgb.mode=static&then=rgb&source=assistant - Pages that need a controller show a “Connect” prompt until one is connected, then open the requested view. ## Pages | Route | Page | What it’s for | Needs | Deep-link params | |---|---|---|---|---| | `#/` | Home | Connect a controller and see its status at a glance. | – | `connect`: Set to 1 to open the controller picker immediately (needs a click in most browsers). | | `#/whats-new` | What’s new | Firmware changes, newest first. With a controller connected, shows what its update brings. | – | `changes`: Changelog section to show: input \| joysticks \| snapback \| motion \| rgb \| haptics \| battery \| wireless \| modes \| system | | `#/input` | Input | Remap buttons per output mode, analog trigger thresholds and rapid trigger. | Controller | `mode`: Output profile to edit (wii-* only on controllers with Wii mode): switch \| xinput \| snes \| n64 \| gamecube \| sinput \| wii-nunchuk (alias upright) \| wii-sideways \| wii-classic
`input`: Input to open in the editor: INPUT_CODE name (e.g. south, lt_analog), the build’s input name, or its number
`tab`: remap \| calibrate | | `#/joysticks` | Joysticks | Calibrate sticks, set deadzones, response curve, invert axes and angle maps. | Controller + analog | `stick`: left \| right
`tab`: Sub-view to open: calibrate \| sensitivity \| angles \| axes (deadzone = old alias) | | `#/snapback` | Snapback | Tune the snapback filter that removes stick "bounce" when you let go. | Controller + analog | `stick`: left \| right | | `#/motion` | Motion | Gyro and accelerometer calibration, sensitivity and live view. | Controller + imu | – | | `#/rgb` | RGB | LED colors per group, effects, speed and brightness. | Controller + rgb | – | | `#/haptics` | Haptics | Rumble strength, trigger haptics and a feedback test. | Controller + haptics | – | | `#/battery` | Battery | Battery, charger (PMIC) and fuel gauge status. | Controller + battery | – | | `#/wireless` | Wireless | Bluetooth pairing info, wireless module firmware and WLAN dongle settings. | Controller + wireless | `update`: Set to 1 to open the wireless module update dialog
`baud`: esptool baud rate override (default 115200) | | `#/gamepad` | Gamepad | Default mode, Switch body colors, MAC address and device info. | Controller | – | | `#/user` | User | Your player name stored on the controller. | Controller | – | | `#/firmware` | Firmware | Update firmware, install HOJA on a blank board, or recover a controller. | – | `build`: Build id to preselect for install (e.g. gcu_2, progcc_3.2) | | `#/arena` | Arena | Gameplay testing arena: try your connected controller in a platform-fighter sandbox. | Controller | `tab`: play \| help
`mode`: free \| targets (help also opens that tab) | | `#/platformer` | 3D Platformer | Run, jump, long jump, ground pound and wall kick around a small 3D test course with your controller. | Controller | `tab`: play \| help | | `#/settings` | App settings | Theme (dark, light or system), motion, install and updates. | – | `theme`: dark \| light \| system | | `#/about` | Help & about | Troubleshooting, version info and attributions. | – | `guide`: linux \| connect \| ios (opens that guide) | Capability flags used in “Needs” columns (detected from the connected controller): - `analog`: the controller has at least one analog stick - `battery`: the controller has a battery - `hapticHD`: the controller has HD (linear) haptics - `haptics`: the controller has rumble (HD or standard) - `imu`: the controller has a gyro/accelerometer (IMU) - `imuModeWii`: the controller has a gyro/accelerometer (IMU), Wii mode, and firmware that can turn motion on or off per output mode - `imuModes`: the controller has a gyro/accelerometer (IMU) and firmware that can turn motion on or off per output mode - `invertAllowed`: the firmware allows inverting stick axes - `leftStick`: the controller has a left analog stick - `rgb`: the controller has RGB LEDs - `rightStick`: the controller has a right analog stick - `splitDefaults`: capability "splitDefaults" - `wii`: the controller supports Wii mode (Wii Remote over Bluetooth, RM2 wireless module) - `wireless`: the controller has wireless (Bluetooth) hardware - `wlan`: the controller supports the WLAN dongle ## Settings catalog 72 settings can be read and proposed by key. Anything not listed (calibration, button remapping, angle maps, pairing, firmware) is done interactively on its page. ### Joysticks (`joysticks`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `joysticks.leftDeadzone` | Center deadzone | number 0% – 48%, step 0.1 | leftStick | How far the left stick must move before it registers, as a % of full travel. | | `joysticks.leftOuterDeadzone` | Edge deadzone | number 0% – 48%, step 0.1 | leftStick | How close to the rim the left stick reaches full output, as a % of full travel. | | `joysticks.leftCurve` | Response curve | number 0.5 × – 3 ×, step 0.01 | leftStick | Exponent applied to the left stick’s output. 1.00 is linear. | | `joysticks.leftInvertX` | Invert LX | on \| off | invertAllowed | Flip the left stick’s horizontal (left ↔ right) direction. | | `joysticks.leftInvertY` | Invert LY | on \| off | invertAllowed | Flip the left stick’s vertical (up ↔ down) direction. | | `joysticks.rightDeadzone` | Center deadzone | number 0% – 48%, step 0.1 | rightStick | How far the right stick must move before it registers, as a % of full travel. | | `joysticks.rightOuterDeadzone` | Edge deadzone | number 0% – 48%, step 0.1 | rightStick | How close to the rim the right stick reaches full output, as a % of full travel. | | `joysticks.rightCurve` | Response curve | number 0.5 × – 3 ×, step 0.01 | rightStick | Exponent applied to the right stick’s output. 1.00 is linear. | | `joysticks.rightInvertX` | Invert RX | on \| off | invertAllowed | Flip the right stick’s horizontal (left ↔ right) direction. | | `joysticks.rightInvertY` | Invert RY | on \| off | invertAllowed | Flip the right stick’s vertical (up ↔ down) direction. | ### Snapback (`snapback`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `snapback.leftType` | Filter mode | 0 = Low-pass (also: lpf, low-pass filter, lowpass, filter, default); 1 = Auto (also: automatic, detect); 2 = Off (also: disabled, none, raw) | leftStick | How the left stick suppresses the rebound past center after you let go. | | `snapback.leftIntensity` | Filter cutoff | number 30 Hz – 150 Hz, step 0.5 | leftStick | Low-pass cutoff frequency for the left stick. Lower = stronger smoothing. | | `snapback.rightType` | Filter mode | 0 = Low-pass (also: lpf, low-pass filter, lowpass, filter, default); 1 = Auto (also: automatic, detect); 2 = Off (also: disabled, none, raw) | rightStick | How the right stick suppresses the rebound past center after you let go. | | `snapback.rightIntensity` | Filter cutoff | number 30 Hz – 150 Hz, step 0.5 | rightStick | Low-pass cutoff frequency for the right stick. Lower = stronger smoothing. | ### Motion (`motion`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `motion.enabled` | Motion (all modes) | on \| off | imu | Turn the gyro and accelerometer on or off for every game. | | `motion.switchMotion` | Switch motion | on \| off | imuModes | Motion in Switch mode. Only applies while Motion (all modes) is on. | | `motion.steamMotion` | Steam motion | on \| off | imuModes | Motion in Steam (SInput) mode. Only applies while Motion (all modes) is on. | | `motion.wiiMotion` | Wii motion | on \| off | imuModeWii | Motion in Wii mode. Only applies while Motion (all modes) is on. | | `motion.gyroSensitivityX` | X axis | number 0.5 × – 2 ×, step 0.01 | imu | Gyro X-axis multiplier (default 1.20×). | | `motion.gyroSensitivityY` | Y axis | number 0.5 × – 2 ×, step 0.01 | imu | Gyro Y-axis multiplier (default 1.20×). | | `motion.gyroSensitivityZ` | Z axis | number 0.5 × – 2 ×, step 0.01 | imu | Gyro Z-axis multiplier (default 1.20×). | | `motion.accelSensitivityX` | X axis | number 0.5 × – 2 ×, step 0.01 | imu | Accelerometer X-axis multiplier (default 1.00×). | | `motion.accelSensitivityY` | Y axis | number 0.5 × – 2 ×, step 0.01 | imu | Accelerometer Y-axis multiplier (default 1.00×). | | `motion.accelSensitivityZ` | Z axis | number 0.5 × – 2 ×, step 0.01 | imu | Accelerometer Z-axis multiplier (default 1.00×). | ### RGB (`rgb`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `rgb.mode` | Effect | 0 = Authentic (also: authentic, chroma, auto, era, classic); 1 = Static (also: static, user, solid, none, custom); 2 = Rainbow (also: rainbow, cycle, spectrum); 3 = React (also: react, reactive, press); 4 = Fairy (also: fairy, twinkle, sparkle) | rgb | Lighting effect: Authentic (classic face-button colors for the output mode; called Chroma in older apps), Static (your colors), Rainbow, React (flash on press) or Fairy (blend between your first six colors). | | `rgb.brightness` | Brightness | number 0% – 100% | rgb | How bright the LEDs are, from off to full. | | `rgb.speed` | Animation time | number 300 ms – 5000 ms, step 25 | rgb | How long one animation step or fade takes, in milliseconds. Lower is faster. | | `rgb.idleGlow` | Idle glow | on \| off | rgb | After 5 minutes without input the lights go dark and a single LED glows to show battery status. Any input turns it off again. Turn this off to keep it dark too. | | `rgb.allColors` | All group colors | hex color like #ff8800 | rgb | Set every LED group to the same color at once (like hoja2’s “Paste All”). | | `rgb.group1Color` | Group 1 color | hex color like #ff8800 | rgb | Color of LED group 1 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group2Color` | Group 2 color | hex color like #ff8800 | rgb | Color of LED group 2 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group3Color` | Group 3 color | hex color like #ff8800 | rgb | Color of LED group 3 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group4Color` | Group 4 color | hex color like #ff8800 | rgb | Color of LED group 4 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group5Color` | Group 5 color | hex color like #ff8800 | rgb | Color of LED group 5 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group6Color` | Group 6 color | hex color like #ff8800 | rgb | Color of LED group 6 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group7Color` | Group 7 color | hex color like #ff8800 | rgb | Color of LED group 7 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group8Color` | Group 8 color | hex color like #ff8800 | rgb | Color of LED group 8 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group9Color` | Group 9 color | hex color like #ff8800 | rgb | Color of LED group 9 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group10Color` | Group 10 color | hex color like #ff8800 | rgb | Color of LED group 10 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group11Color` | Group 11 color | hex color like #ff8800 | rgb | Color of LED group 11 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group12Color` | Group 12 color | hex color like #ff8800 | rgb | Color of LED group 12 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group13Color` | Group 13 color | hex color like #ff8800 | rgb | Color of LED group 13 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group14Color` | Group 14 color | hex color like #ff8800 | rgb | Color of LED group 14 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group15Color` | Group 15 color | hex color like #ff8800 | rgb | Color of LED group 15 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group16Color` | Group 16 color | hex color like #ff8800 | rgb | Color of LED group 16 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group17Color` | Group 17 color | hex color like #ff8800 | rgb | Color of LED group 17 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group18Color` | Group 18 color | hex color like #ff8800 | rgb | Color of LED group 18 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group19Color` | Group 19 color | hex color like #ff8800 | rgb | Color of LED group 19 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group20Color` | Group 20 color | hex color like #ff8800 | rgb | Color of LED group 20 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group21Color` | Group 21 color | hex color like #ff8800 | rgb | Color of LED group 21 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group22Color` | Group 22 color | hex color like #ff8800 | rgb | Color of LED group 22 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group23Color` | Group 23 color | hex color like #ff8800 | rgb | Color of LED group 23 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group24Color` | Group 24 color | hex color like #ff8800 | rgb | Color of LED group 24 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group25Color` | Group 25 color | hex color like #ff8800 | rgb | Color of LED group 25 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group26Color` | Group 26 color | hex color like #ff8800 | rgb | Color of LED group 26 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group27Color` | Group 27 color | hex color like #ff8800 | rgb | Color of LED group 27 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group28Color` | Group 28 color | hex color like #ff8800 | rgb | Color of LED group 28 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group29Color` | Group 29 color | hex color like #ff8800 | rgb | Color of LED group 29 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group30Color` | Group 30 color | hex color like #ff8800 | rgb | Color of LED group 30 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group31Color` | Group 31 color | hex color like #ff8800 | rgb | Color of LED group 31 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | | `rgb.group32Color` | Group 32 color | hex color like #ff8800 | rgb | Color of LED group 32 as listed on the RGB page (group names and count depend on the controller, e.g. "D-Pad" or "A"). | ### Haptics (`haptics`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `haptics.intensity` | Intensity | number 0% – 100% | haptics | How strong rumble feels, from off to full power. | | `haptics.triggerFeedback` | Trigger haptics | on \| off | hapticHD | Pulse when an analog trigger passes its activation threshold or rapid-trigger delta (set in Input). | ### Wireless (`wireless`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `wireless.dongleKey` | WLAN dongle PIN | number 0 – 9999 | wlan | Four-digit pairing PIN (0000–9999). Set the same PIN on your WLAN dongle so they pair. | ### Gamepad (`gamepad`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `gamepad.defaultMode` | Default mode | 254 = Auto (also: auto, automatic, detect) (needs splitDefaults); 0 = Switch (also: switch, swpro, pro, nintendo switch, switch pro); 1 = XInput (also: xinput, xbox, x-input, pc); 2 = Slippi (also: slippi, dolphin, melee); 3 = GameCube (also: gamecube, gc, gcube, ngc); 4 = N64 (also: n64, nintendo 64); 5 = SNES (also: snes, sfc, super famicom, super nintendo, nes); 6 = Steam (also: steam, sinput, s-input); 7 = Wii (also: wii, wiimote, wii remote) (needs wii) | – | The output mode the controller starts in. On firmware with separate defaults this is the one used when plugged in, and Auto detects the console or PC. Only Switch and Steam modes work with this config app. After changing it, hold A or B while plugging in to come back here. | | `gamepad.defaultWireless` | Wireless default | 254 = Auto (also: auto, automatic, detect); 0 = Switch (also: switch, swpro, pro, nintendo switch, switch pro); 6 = Steam (also: steam, sinput, s-input); 7 = Wii (also: wii, wiimote, wii remote) (needs wii) | splitDefaults | The mode the controller starts in on battery (firmware with separate defaults). Auto connects to whichever saved console or PC answers first: Switch, then Wii, then PC. | | `gamepad.bodyColor` | Body color | hex color like #ff8800 | – | Main shell color the Switch shows in its menus and some games. | | `gamepad.buttonsColor` | Buttons color | hex color like #ff8800 | – | Color of the buttons as drawn by the Switch. | | `gamepad.leftGripColor` | Left grip color | hex color like #ff8800 | – | Left handle color as drawn by the Switch. | | `gamepad.rightGripColor` | Right grip color | hex color like #ff8800 | – | Right handle color as drawn by the Switch. | | `gamepad.webusbPopup` | WebUSB popup | on \| off | – | Show the browser’s “open the config app” notification when the controller is plugged in. | ### User (`user`) | Key | Label | Values | Needs | Description | |---|---|---|---|---| | `user.name` | Player name | text, up to 24 bytes | – | Stored on the controller. Up to 24 characters. | ## Safety - Links never change anything silently. Opening an `#/apply` link shows a “before → after” list and the user must press **Apply** or **Apply & save**; **Cancel** discards it. - The controller must be connected over USB in a Chromium-based browser (Chrome, Edge, Opera, Brave… on desktop or Android). iPhone/iPad browsers cannot connect to controllers. - Changes apply to the controller immediately but are only kept after a power cycle once saved (**Save** in the top bar, or **Apply & save**). - Settings the connected controller doesn’t have hardware for are shown as “Not supported” and skipped. - Firmware installs, calibration, button remapping and pairing are deliberately *not* settable by link. Send the user to the page instead (e.g. `#/joysticks?stick=left`). - Assistants should explain what a link will change before sharing it and never claim a change was made, because only the user can confirm it. ## Docs - [Deep links (human guide)](https://handheldlegend.github.io/hoja3/docs/DEEPLINKS.md): every page, parameter and setting with examples. - [Troubleshooting knowledge base](https://handheldlegend.github.io/hoja3/docs/KNOWLEDGE.md): connecting, browsers, calibration, drift, snapback, firmware updates, recovery, wireless, battery, saving, iOS. - [MCP server](https://github.com/HandHeldLegend/handheldlegend.github.io/tree/master/hoja3/mcp): a zero-dependency Model Context Protocol server (`node hoja3/mcp/server.mjs`) exposing list_pages, list_settings, describe_setting, build_page_link, build_settings_link, troubleshoot and list_firmware_builds. ## Optional - In-page bridge: when the app is open, `window.hhl` offers status(), listPages(), listSettings(), getSetting(key), navigate(page, params) and proposeSettings({key: value}) (always asks the user to confirm). The same tools are registered with the browser’s WebMCP API (`navigator.modelContext`) when available. - [Source code](https://github.com/HandHeldLegend/handheldlegend.github.io/tree/master/hoja3)