Skip to content

Controls

All keyboard and gamepad input is configurable. This page documents the defaults and the two ways to change them.

Default bindings

Player 1

NES button Default key
D-pad Up ArrowUp
D-pad Down ArrowDown
D-pad Left ArrowLeft
D-pad Right ArrowRight
A Z
B X
Select Right Shift
Start Enter

Player 2

NES button Default key
D-pad Up W
D-pad Down S
D-pad Left A
D-pad Right D
A Q
B E
Select L
Start P

Player 2 input is fully wired — these keys drive the second controller ($4017). A second USB gamepad can also drive Player 2 (see Gamepads).

Players 3 & 4 (Four Score)

The Four Score 4-player adapter is off by default. Toggle it with the "Four Score (4-player)" checkbox in the in-app input modal (open the debugger with ~, tick Input). While off, the emulator is byte-for-byte a standard two-controller NES; while on, Players 3 and 4 are multiplexed onto $4016/$4017 for the handful of games that support it. Default keys:

NES button Player 3 Player 4
D-pad Up I Numpad8
D-pad Down K Numpad2
D-pad Left J Numpad4
D-pad Right L Numpad6
A U Numpad7
B O Numpad9
Select M Numpad1
Start . (Period) Numpad3

All four players are rebindable (keyboard and gamepad) in the same input modal, and a third/fourth USB gamepad auto-binds to Players 3/4 — most 4-player setups use gamepads, so the keyboard defaults above are a fallback.

System keys

Action Default key Notes
Pause / Resume Space Toggle emulation; disabled while a netplay session is active
Quit / exit fullscreen Esc Closes the window cleanly (or leaves fullscreen first)
Save state F1 Writes to the active slot for the current ROM
Load state F4 Reads from the active slot
Rewind F5 Held — step back one frame per held tick
Fast-forward Tab Held — runs the emulator unthrottled with audio muted; releasing resumes normal speed
Frame-advance \ (Backslash) Press — steps exactly one frame (for use while paused)
Reset F2 Warm reset (same as the cartridge's RESET button)
Power cycle F3 Cold boot (clears RAM, re-runs init)
Speed up = (Equal) Step up to the next emulation-speed preset
Speed down - (Minus) Step down to the previous emulation-speed preset
Speed reset 0 (Digit0) Reset the emulation speed back to 100%
Movie record F6 Toggle TAS movie recording (start = power-on; stop = save .rnm)
Movie play F7 Toggle TAS movie playback (start = open .rnm; stop = live input)
Movie branch F8 Branch the current run into a new recording at this frame
Swap disk side F9 Cycle the inserted FDS disk side (FDS games only)
Insert coin F10 Insert a Vs. System coin into acceptor #1 (Vs. games only)
Fullscreen F11 Toggle borderless fullscreen (native only)
Open ROM F12 Open the file picker to load a different .nes ROM
Toggle menu bar M Show / hide the always-on menu bar
Debugger overlay ` (Backquote, the ~ key) Toggles the egui overlay

Esc cancels the in-app rebind capture too — if you click "rebind" in the debugger and want to back out, press Esc instead of any other key.

Every system action above also has a menu entry (with its accelerator hint shown in the menu), so you never need the hotkeys. See the Menu reference for the full menu tour.

Two ways to rebind

  1. Open View → Settings… from the menu bar and select the Input tab (or press ~ for the debugger overlay and tick its Input checkbox — both surface the same rebind panel).
  2. The panel lists every action — Player 1, Player 2, Players 3 & 4, gamepad (per player), and system keys — with its current binding.
  3. Click rebind next to the action you want to change.
  4. The next key (or, for a gamepad row, the next pad button) you press becomes the new binding. Press Esc to cancel.
  5. Click Save to disk to persist the change.

"Reset to defaults" reverts every binding to the table above. The change is in-memory only until you also click "Save to disk".

2. Editing config.toml by hand

The config file lives at:

OS Path
Linux $XDG_CONFIG_HOME/RustyNES/config.toml (or ~/.config/RustyNES/config.toml)
macOS ~/Library/Application Support/dev.DoubleGate.RustyNES/config.toml
Windows %APPDATA%\DoubleGate\RustyNES\config\config.toml

A minimal example overriding only Player 1's A and B:

[input.player1]
a = "KeyA"
b = "KeyS"

All other fields fall back to the defaults documented above. A missing file is fine — the emulator creates one with defaults the first time you save settings from the in-app modal.

Key names

Key names follow the winit::keyboard::KeyCode enum:

Class Examples
Letters KeyA, KeyB, ... KeyZ
Digits (top row) Digit0 ... Digit9
Function keys F1 ... F12
Arrows ArrowUp, ArrowDown, ArrowLeft, ArrowRight
Modifiers ShiftLeft, ShiftRight, ControlLeft, ControlRight, AltLeft, AltRight, SuperLeft, SuperRight
Whitespace + punctuation Space, Enter, Tab, Backspace, Escape, Comma, Period, Slash, Backslash, Semicolon, Quote, Backquote, Minus, Equal, BracketLeft, BracketRight
Numpad Numpad0 ... Numpad9

Key names are case-sensitive and use the physical key layout (so KeyA is the position of the A key on a US QWERTY keyboard regardless of your OS-level keymap). A typo in config.toml logs a warning to stderr and the binding is silently dropped — the rest of the file still loads.

Held versus tap

  • D-pad and A/B/Select/Start are held: held keys produce held NES buttons; releasing the key clears the bit.
  • Pause / Resume, Save / Load state, Reset, Power cycle, Debugger toggle fire on key-down only.
  • Rewind is held: while F5 is held, the emulator walks backwards through the rewind ring one frame per redraw. Releasing resumes forward play.
  • Fast-forward is held: while Tab is held, the emulator runs unthrottled (audio is muted to avoid pops); releasing resumes normal speed.
  • Frame-advance (\) fires on key-down and steps exactly one frame — meant for use while emulation is paused.
  • Quit (Esc) fires on key-down.

TAS movies (record / playback)

RustyNES can record a movie — the per-frame controller input applied on top of a reproducible start point — and replay it bit-for-bit. Because the core is deterministic, a replayed movie re-derives every pixel and audio sample exactly.

  • Record (F6): press once to start. Recording power-cycles the console so the movie begins from a fresh boot (the most portable start point). Press F6 again to stop; a file picker prompts for a .rnm save path (defaults to the movies/ folder under your data directory — see File locations).
  • Play (F7): press once to open a .rnm file. The console seeks to the movie's start point and replays it; the movie's input overrides your live keyboard and gamepad input until the movie ends (control then returns to live input automatically) or you press F7 again to stop.
  • Branch (F8): while watching a replay (or at any point during live play), press F8 to begin recording a new movie from the current state. This embeds a save-state start point so you can diverge from a run and record your own continuation. Stop with F6 to save the branch.

When the debugger overlay (`) is open, the top toolbar shows a read-only status indicator: REC N frames while recording, or PLAY n/N during playback. The overlay never affects the recording — it is purely an observer, so a movie recorded with the overlay open replays identically with it closed.

A .rnm movie is tied to the ROM it was recorded against (by SHA-256); loading a movie while a different ROM is running reports a mismatch and declines to play.

Web build note: the browser build supports .rnm movie record / playback as well, using the File System Access API (with a download / upload fallback) for the file I/O. TAStudio piano-roll editing remains a native-only tool.

Gamepads

USB / Bluetooth gamepads are supported via gilrs. The first pad your OS reports drives Player 1; a second distinct pad drives Player 2. The default layout is Xbox-style:

NES button Default pad input
A South (A / cross)
B West (X / square)
Start Start
Select Back / View / Select
D-pad D-pad
D-pad (alt) Left analog stick, past the deadzone

Rebind pad buttons in the same in-app modal as the keyboard (open the debugger with ~, tick Input): each player has a row of gamepad bindings — click rebind and press the pad button you want. The left analog stick doubles as a D-pad once it deflects past axis_deadzone (default 0.5). Bindings persist to the [input.gamepad1] / [input.gamepad2] sections of config.toml (see Configuration). The browser build supports standard-mapping gamepads too, via the browser Gamepad API (the first connected pad drives Player 1); its bindings are fixed to the Xbox-style layout above.

See also