Function that returns new emulator instance.
options - initialization options. See the following documentation sections for their description. Options with undefined
value will be ignored.
let nes;
// No options specified
nes = cfxnes();
// All options and their default values
nes = cfxnes({
region: 'auto',
speed: 1,
rom: undefined,
JSZip: undefined,
video: {
output: null,
renderer: 'webgl',
scale: 1,
palette: 'fceux',
filter: 'nearest',
debug: false,
},
fullscreen: {
type: 'maximized',
}
audio: {
enabled: true,
volume: {
master: 0.5,
pulse1: 1,
pulse2: 1,
triangle: 1,
noise: 1,
dmc: 1,
},
},
devices: {
1: 'joypad',
2: 'zapper',
},
inputs: {
'1.joypad.a': 'keyboard.x',
'1.joypad.b': ['keyboard.y', 'keyboard.z'],
'1.joypad.start': 'keyboard.enter',
'1.joypad.select': 'keyboard.shift',
'1.joypad.up': 'keyboard.up',
'1.joypad.down': 'keyboard.down',
'1.joypad.left': 'keyboard.left',
'1.joypad.right': 'keyboard.right',
'2.zapper.trigger': 'mouse.left',
},
});
Name | Type | Writable | Default | Description |
---|---|---|---|---|
version | string |
no | cfxnes version. | |
logLevel | string |
yes | 'warn' |
Verbosity of logging for all emulator instances.'off' - Logging is disabled.'error' - Log errors.'warn' - Log errors and warnings.'info' - Log errors, warnings and info messages. |
cfxnes.version; // '0.4.0', '1.0.0', '1.2.1', etc.
cfxnes.logLevel = 'off'; // Disable logging
cfxnes.logLevel = 'info'; // Log everything
Emulator instance returned by the cfxnes
function.
Name | Type | Writable | Default | Description |
---|---|---|---|---|
running | boolean |
no | false |
true when emulator is running, false otherwise. |
fps | number |
no | Number of frames per second of running emulator. | |
region | string |
yes | 'auto' |
Emulated NES region.'auto' - Automatic region detection (not very reliable).'ntsc' - NTSC region (60 FPS).'pal' - PAL region (50 FPS). |
speed | number |
yes | 1 |
Emulation speed multiplier. It must be larger than 0. |
nvram | Uint8Array |
no | null | Provides access to NVRAM. NVRAM (Non-Volatile RAM) is a memory that is usually battery-backed and serves as a place for game saves. NVRAM is only used by some games (e.g., The Legend of Zelda or Final Fantasy). The property is null when NVRAM is not available. |
rom | object |
no | ROM module | |
video | object |
no | Video module | |
fullscreen | object |
no | Fullscreen module | |
audio | object |
no | Audio module | |
devices | object |
no | Devices module | |
inputs | object |
no | Inputs module | |
config | object |
no | Configuration module |
Signature | Description |
---|---|
start() | Starts emulator. |
stop() | Stops emulator. |
step() | Forces emulator to render one frame. |
power() | HW reset (NES power button). |
reset() | SW reset (NES reset button). |
const nes = cfxnes();
nes.region = 'pal'; // Set PAL region
nes.speed = 1.5; // Set 1.5x emulation speed
nes.running; // false
nes.start(); // Start emulator
nes.running; // true
nes.stop(); // Stop emulator
nes.running; // false
nes.power(); // HW reset
nes.reset(); // SW reset
if (nvram.data) {
nvram.data.fill(0); // Clear NVRAM of the currently running game
}
Module that can load ROM images into emulator.
Supported ROM image formats are iNES and NES 2.0. Cfxnes can also load zipped ROM image when JSZip library (^3.1.0) is present. JSZip can be provided either through global variable window.JSZip
or using JSZip
initialization option.
cfxnes({JSZip});
ROM image can be loaded in two ways:
- Use the
rom
initialization option. This will load ROM image from the specified source and then automatically starts execution. All loading errors will be logged usingconsole.error
. - Call the
nes.rom.load()
method. This allows more precise control over the loading process.
// 1. Use the 'rom' initialization option
const nes = cfxnes({rom: source});
// 2. Call the nes.rom.load() method
nes.rom.load(source)
.then(() => {/* handle success */})
.catch(error => {/* handle error */})
The source can be Uint8Array
, ArrayBuffer
, Array
, Blob
or string
. String value is interpreted as URL of a ROM image.
Name | Type | Writable | Default | Description |
---|---|---|---|---|
loaded | boolean |
no | false |
true when a ROM image is loaded, false otherwise. |
sha1 | string |
no | null |
SHA-1 of loaded ROM image, null otherwise. |
Signature | Returns | Description |
---|---|---|
load(source) | Promise |
Loads ROM image from the specified source. |
unload() | Unloads loaded ROM image. |
const nes = cfxnes();
const {rom} = nes;
rom.loaded; // false
rom.sha1; // null
// Load ROM image from relative URL
rom.load('data/game.nes').then(() => {
rom.loaded; // true
rom.sha1; // SHA-1 of the loaded image
rom.unload(); // Unload the image
rom.loaded; // false;
}).catch(error => {
console.error('Oops!', error);
})
// Load ROM image from memory or blob
var data = getData(); // Uint8Array, ArrayBuffer, Array, Blob
rom.load(data).then(/* ... */);
Display settings module.
Cfxnes requires a canvas
element to render its video output. The canvas can be specified in several ways:
- Create canvas element with
cfxnes
ID. It will be automatically used during cfxnes initialization. - Use the
video.output
initialization option. - Use the
nes.video.output
property.
<canvas id="cfxnes"><canvas>
<script>
const canvas = document.getElementById('cfxnes');
// 1. There is a canvas element with 'cfxnes' ID.
let nes = cfxnes();
nes.video.output === canvas; // true
// 2. Use the 'video.output' initialization option.
nes = cfxnes({
video: {output: canvas}
});
// 3. Use the 'nes.video.output' property
nes.video.output = canvas;
</script>
In case there is no ROM image loaded, running emulator will display white noise as its video output.
Once the output is set, it is not possible to change value of the renderer
property. To change renderer, you need to use a different canvas with uninitialized context (see example bellow).
Name | Type | Writable | Default | Description |
---|---|---|---|---|
output | HTMLCanvasElement |
yes | null |
Canvas element used to render emulator video output. The property can be set to null to disable rendering. |
renderer | string |
yes | 'webgl' |
Rendering back-end.'canvas' - Renderer using Canvas API. It is used as fallback when WebGL is not available.'webgl' - Renderer using WebGL. It should be faster than the 'canvas' renderer, but this highly depends on browser, OS, graphic card driver, etc. |
palette | string |
yes | 'fceux' |
Palette used for generating RGB color values. Allowed values are:'asq-real-a' , 'asq-real-b' , 'bmf-fin-r2' , 'bmf-fin-r3' , 'fceu-13' , 'fceu-15' , 'fceux' , 'nestopia-rgb' , 'nestopia-yuv' , 'sony-cxa2025as' , 'unsaturated-v6' See FCEUX documentation for their description. |
scale | number |
yes | 1 |
Canvas resolution multiplier. It must be larger than 0. Non-integer value might cause visual artifacts due to upscaling. The base resolution is 256x240. |
filter | string |
yes | 'nearest' |
Filter used during upscaling.'nearest' - Nearest-neighbor interpolation.'linear' - Linear interpolation. |
debug | boolean |
yes | false |
Enables additional video output (content of pattern tables and background/sprite palettes) to be rendered on canvas. This will also double width of the canvas. |
Signature | Returns | Description |
---|---|---|
clear() | void |
Clears canvas. |
const nes = cfxnes();
const {video} = nes;
video.renderer = 'webgl'; // Renderer can be only changed before the output is set
video.output = document.getElementById('canvas'); // Set output
video.palette = 'nestopia-rgb'; // Set palette
video.scale = 2; // Set 2x resolution scale
video.filter = 'linear'; // Linear interpolation
video.debug = true; // Enable debug output
// To change renderer, we need a different canvas with uninitialized context
video.output = null; // Disconnect the currently used canvas
video.renderer = 'canvas'; // Change the renderer
video.output = document.getElementById('canvas-2'); // Use a different canvas
Fullscreen module.
It is recommended to wrap used canvas
element in extra div
to make fullscreen working properly.
Name | Type | Writable | Default | Description |
---|---|---|---|---|
is | boolean |
no | false | true when emulator is in fullscreen mode, false otherwise. |
type | string |
yes | 'maximized' |
Type of fullscreen mode.'maximized' - Maximizes output resolution while keeping its original aspect ratio.'normalized' - Same as the 'maximized' type, but output resolution is integer multiple of the base resolution 256x240. This should reduce visual artifacts caused by upscaling.'stretched' - Output is stretched to fill the whole screen (both horizontally and vertically). The original aspect ratio is not preserved. |
Signature | Returns | Description |
---|---|---|
enter() | Promise |
Switches to fullscreen mode. The method does nothing when fullscreen is on. |
exit() | Promise |
Returns from fullscreen mode. The method does nothing when fullscreen is off. |
const nes = cfxnes();
const {fullscreen} = nes;
fullscreen.is; // false
fullscreen.type = 'stretched'; // Make fullscreen 'stretched'
// Note: Due to security reasons, browsers typically block fullscreen
// requests that are not tied to user input (e.g., button click).
fullscreen.enter().then(() => {
fullscreen.is; // true
}).catch(error => {
console.error('Oops!', error); // Browser refused fullscreen request
});
Audio settings module.
The nes.audio
property is null
when browser does not support Web Audio (currently only IE 11 and older).
Name | Type | Writable | Default | Description |
---|---|---|---|---|
enabled | boolean |
yes | true | true when audio output is enabled, false otherwise. |
volume | object |
no | Audio volume configuration. | |
volume.master | number |
yes | 0.5 |
Master volume. |
volume.pulse1 | number |
yes | 1 |
Pulse channel #1 volume. |
volume.pulse2 | number |
yes | 1 |
Pulse channel #2 volume. |
volume.triangle | number |
yes | 1 |
Triangle channel volume. |
volume.noise | number |
yes | 1 |
Noise channel volume. |
volume.dmc | number |
yes | 1 |
DMC channel volume. |
const nes = cfxnes();
const {audio} = nes;
if (audio) {
audio.enabled = false; // Disable audio
audio.volume.master = 1; // Maximize the overall volume
audio.volume.dmc = 0; // Mute the DMC channel
} else {
console.log('Web Audio not supported!');
}
Module that allows to set up input devices.
NES has 2 input ports, each of them can be assigned a device through numeric property. Allowed values are:
'joypad'
- Standard NES controller.'zapper'
- NES Zapper (beam gun).null
- No device.
Number | Type | Writable | Default | Description |
---|---|---|---|---|
1 | string |
yes | 'joypad' |
Device connected to port #1. |
2 | string |
yes | 'zapper' |
Device connected to port #2. |
const nes = cfxnes();
const {devices} = nes;
devices[1] = 'zapper'; // Set zapper on port #1
devices[2] = null; // Make port #2 empty
Module that allows to set up input controls.
There are 2 kinds of input devices:
- The ones being emulated (see
nes.devices
section). We refer to them as devices. - The real ones (keyboard, mouse, gamepad, etc.). We refer to them as sources.
Input of any device can be expressed as a string '<port>.<device>.<name>'
:
<port>
- port (1
or2
)<device>
- device ('joypad'
or'zapper'
)<name>
- name of the input
Input of any source can be expressed as a string '<source>.<name>'
:
<source>
- source ('keyboard'
,'mouse'
,'gamepad0'
,'gamepad1'
, ...)<name>
- name of the input
Examples:
'1.joypad.start'
- Start button of a joypad connected to port #1.'2.zapper.trigger'
- Trigger button of a zapper connected to port #2.'keyboard.ctrl'
- Ctrl key.'mouse.left'
- Left mouse button.'gamepad0.start'
- Start button of gamepad #0.'gamepad1.x'
- X button of gamepad #1.
Name | Type | Writable | Description |
---|---|---|---|
state | object |
no | Submodule that holds state of all inputs. - For buttons, the state is boolean . - For '{1,2}.zapper.beam' , the state is array of 2 numbers [x, y] (beam coordinates). |
map | object |
no | Submodule that holds mapping between inputs. |
Signature | Description |
---|---|
state.get(devInput) | Returns state of a device input. |
state.set(devInput, state) | Sets state of a device input. |
map.get(input) | Returns mapping of an input. |
map.set(devInput, srcInput) | Sets mapping between source and device input. |
map.delete(input) | Deletes mapping of an input. |
record(callback) | Registers callback function that will be called when the next source input is received. The callback is immediately dropped after its use. Typical use of this method is to let users customize key bindings. |
const nes = cfxnes();
const {inputs} = nes;
// State
inputs.state.set('1.joypad.a', true); // Press 'A button' of joypad #1
inputs.state.get('1.joypad.a'); // Returns true
// Mapping
inputs.map.delete('1.joypad.a'); // Delete mapping of a device input
inputs.map.get('1.joypad.a'); // Returns []
inputs.map.set('1.joypad.a', 'keyboard.z'); // Map 'A button' of joypad #1 to 'Z' key
inputs.map.set('1.joypad.a', 'keyboard.y'); // Map the same input to 'Y' key
inputs.map.get('1.joypad.a'); // Returns ['keyboard.z', 'keyboard.y']
inputs.map.delete('keyboard.z'); // Delete mapping of a source input
inputs.map.get('1.joypad.a'); // Returns ['keyboard.y']
// Interactive rebinding
const devInput = '1.joypad.a';
showUserMessage('Press key or button...');
inputs.record(srcInput => {
inputs.map.delete(devInput); // Delete previous mapping for each input
inputs.map.delete(srcInput);
inputs.map.set(devInput, srcInput); // Set new mapping
hideUserMessage();
})
Input | Name |
---|---|
A, B buttons | 'a' , 'b' |
Start, Select buttons | 'start' , 'select' |
D-pad buttons | 'left' , 'right' , 'up' , 'down' |
Input | Name |
---|---|
Trigger | 'trigger' |
Beam position | It is permanently mapped to mouse cursor position. |
Input | Name |
---|---|
Character keys (letters) | 'a' , 'b' , ..., 'z' |
Character keys (numbers) | '0' , '1' , ..., '9' |
Character keys (special) | 'space' , ',' , '.' , '/' , ';' , '\'' , '\\' , '[' , ']' , '``' , '-' , '=' |
Function keys | 'f1' , 'f2' , ..., 'f12' |
Modifier keys | 'shift' , 'ctrl' , 'alt' |
Navigation keys | 'left' , 'up' , 'right' , 'down' , 'tab' , 'home' , 'end' , 'page-up' , 'page-down' |
System keys | 'escape' , 'pause' |
Editing keys | 'enter' , 'backspace' , 'insert' , 'delete' |
Lock keys | 'caps-lock' , 'num-lock' , 'scroll-lock' |
Numeric keypad | 'numpad-0' , 'numpad-1' , ..., 'numpad-9' , 'add' , 'subtract' , 'multiply' , 'divide' , 'decimal-point' |
Input | Name |
---|---|
Left, middle, right button | 'left' , 'middle' , 'right' |
The set of inputs that are received from a gamepad depends on whether browser is able to recognize gamepad layout. If the gamepad is correctly recognized, the standard layout is used. Otherwise the generic layout is used as fallback.
Input | Name |
---|---|
A, B, X, Y buttons | 'a' , 'b' , 'x' , 'y' |
Back, Start, Guide buttons | 'back' , 'start' , 'guide' |
D-pad | 'dpad-up' , 'dpad-down' , 'dpad-left' , 'dpad-right' |
Triggers | 'left-trigger' , 'right-trigger' |
Bumpers | 'left-bumper' , 'right-bumper' |
Sticks (buttons) | 'left-stick' , 'right-stick' |
Sticks (axes) | 'left-stick-x' , 'left-stick-y' , 'right-stick-x' , 'right-stick-y' |
To specify axis direction, '+'
or '-'
must be appended (e.g., 'left-stick-x-'
, 'left-stick-x+'
).
Input | Name |
---|---|
Buttons | 'button-0' , 'button-1' , ... |
Axes | 'axis-0' , 'axis-1' , ... |
To specify axis direction, '+'
or '-'
must be appended (e.g., 'axis-0-'
, 'axis-0+'
).
Module that provides access to emulator configuration.
The structure of configuration options corresponds to structure of initialization options (see cfxnes) with exception of rom
, JSZip
, video.output
that are ignored.
Signature | Description |
---|---|
get() | Returns all options and their values. |
use(options) | Applies values of specified options. |
const nes = cfxnes();
const {config} = nes;
const defaults = config.get(); // Get default configuration
config.use({region: 'pal'}); // Update configuration
config.use(defaults); // Restore default configuration