RP2A03 Synth is a work-in-progress NES synthesizer plugin for modern DAWs.
It emulates the NES RP2A03 APU at the register level rather than using samples or approximated chiptune waveforms. It also supports Konami VRC6 and Sunsoft 5B expansion audio.
A FamiTracker-style sequencer, software LFOs, portamento, MIDI, and host automation turn the hardware emulation into a practical software synth.
Alpha software: expect bugs and incomplete features.
"SUNNY" — Snail House cover by recme
Sunny.mp4
demo2.mp4
-
Register-level 2A03 emulation
- Pulse 1/2
- Triangle
- Noise
- Envelope, sweep, length/linear counters, and frame counter
-
VRC6 pulse and sawtooth
-
Sunsoft 5B PSG
-
Band-limited output via
blip_buf -
FamiTracker-style sequences:
- Volume
- Arpeggio
- Pitch
- Hi-pitch
- Duty
- Loop and release points
-
Up to 8-voice polyphony
-
Software vibrato and tremolo
-
Portamento
-
MIDI pitch wheel and pitch-bend sensitivity
-
DAW host automation
-
Adjustable sequence rate from 1–600 Hz
The following parameters are available to the plugin. Delay and fade durations are measured in engine frames; at the default 60 Hz step time, one frame is approximately 16.7 ms.
| Parameter | Range / default | Function |
|---|---|---|
| Sequence Index | 0–127 / 0 | Selects the shared sequence slot used by the envelope lanes. |
| Vibrato Depth | 0–15 / 0 | Sets the pitch modulation depth. |
| Vibrato Speed | 0–63 / 4 | Sets the vibrato phase advance per engine frame. |
| Vibrato Delay | 0–127 frames / 0 | Delays vibrato after a fresh note trigger. |
| Tremolo Depth | 0–15 / 0 | Sets the volume modulation depth. |
| Tremolo Speed | 0–63 / 4 | Sets the tremolo phase advance per engine frame. |
| Tremolo Delay | 0–127 frames / 0 | Delays tremolo after a fresh note trigger. |
| Delay Speed | 0–127 frames / 0 | Shared linear fade-in duration after either delay. Zero starts modulation at full depth. |
| HW Volume | 0–15 / 15 | Sets the channel's hardware volume ceiling. |
| Pitch | -64–63 / 0 | Applies a fine pitch period offset. |
| Hi-Pitch | -64–63 / 0 | Applies a coarse high-byte pitch period offset. |
| Pitch Slide | -8192–8191 / 0 | Applies the host-controlled pitch-wheel offset. |
| Pitch Slide Range | 0–24 semitones / 2 | Sets the pitch-wheel range used by MIDI RPN 0. |
| Step Time | 1–600 Hz / 60 | Sets the sequence and LFO engine frame rate. |
| Polyphony | Off / Off | Enables multi-voice playback. |
| Max Voices | 1–8 / 8 | Limits the number of simultaneous voices when polyphony is enabled. |
| Portamento | Off / Off | Enables glide between successive notes. |
| Portamento Speed | 0–127 / 0 | Sets the speed of enabled portamento. |
| Chip | Waveforms | Examples |
|---|---|---|
| Ricoh 2A03 | Pulse, Triangle, Noise | Most NES games |
| Konami VRC6 | Pulse, Sawtooth | Castlevania III, Madara, Esper Dream 2 |
| Sunsoft 5B | PSG tone + noise | Gimmick!, Batman: Return of the Joker |
The 2A03 DPCM channel is not implemented because it is a sample-playback channel rather than a synthesis channel.
The 5B implementation includes nine selectable tone-duty widths based on the AY8930. This feature was not present on the original Sunsoft 5B, so the S5B mode is intentionally a hybrid.
The 5B hardware envelope is not implemented.
This is a synthesizer inspired by hardware emulation, not a cycle-perfect NES emulator.
The core models the important APU behavior at the register/channel level, but intentionally removes or adds some hardware limitations:
- Polyphony is expanded to 8 voices.
- Triangle volume is controllable.
- Mixing uses conventional linear voice summing.
- Software LFOs and portamento are added.
- Sequence rate is adjustable rather than fixed to 60 Hz.
- Voice allocation uses short ramps to prevent clicks.
- Pitch controls extend beyond raw hardware period registers.
- The 5B adds AY8930-style duty control.
For bit-accurate tracker playback, use FamiStudio, Furnace, or Dn-FamiTracker.
- Rust stable
- Rust 2024 edition support
The core is pure Rust; no C/C++ toolchain is required for the normal builds.
cargo build --releaseRun tests:
cargo test --workspaceBuild the plugin bundles:
cargo xtask bundle rp2a03_niceplug --releaseOutput:
target/release/
target/bundled/
Uses the default OpenGL backend.
cargo xtask bundle rp2a03_niceplug --releaseInstall:
| Format | Location |
|---|---|
.vst3 |
%COMMONPROGRAMFILES%\VST3\ |
.clap |
%COMMONPROGRAMFILES%\CLAP\ |
Linux builds use wgpu/Vulkan.
Install the development dependencies:
sudo apt-get install -y \
libasound2-dev libgl1-mesa-dev libx11-dev libxcursor-dev \
libxrandr-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev libxcb1-devBuild:
cargo xtask bundle rp2a03_niceplug --release \
--no-default-features --features wgpuInstall:
| Format | Location |
|---|---|
.vst3 |
~/.vst3/ |
.clap |
~/.clap/ |
macOS uses wgpu/Metal.
For a universal Intel + Apple Silicon build:
rustup target add x86_64-apple-darwin aarch64-apple-darwin
cargo xtask bundle-universal rp2a03_niceplug --release \
--no-default-features --features wgpuThe normal bundle command can be used for a single architecture.
Build the AU wrapper after building the CLAP:
cmake -B build/auv2 -S packaging/auv2
cmake --build build/auv2 --config ReleaseOutput:
build/auv2/RP2A03 Synth.component
The release build wraps the CLAP using clap-wrapper:
cmake -B build/vst3-macos -S packaging/vst3-macos
cmake --build build/vst3-macos --config ReleaseInstall:
| Format | Location |
|---|---|
.vst3 |
/Library/Audio/Plug-Ins/VST3/ |
.clap |
/Library/Audio/Plug-Ins/CLAP/ |
.component |
/Library/Audio/Plug-Ins/Components/ |
Because development builds are ad-hoc signed, downloaded macOS releases may require:
xattr -dr com.apple.quarantine "/Library/Audio/Plug-Ins/VST3/RP2A03 Synth.vst3"RP2A03-SYNTH/
├── rp2a03_core/ # APU and expansion-chip emulation
├── rp2a03_common/ # MIDI and shared GUI logic
├── rp2a03_niceplug/ # CLAP/VST3 plugin
├── xtask/ # Build and packaging tools
├── packaging/ # AUv2/VST3 wrappers
├── readme_assets/
├── Cargo.toml
└── README.md
The emulation core is independent of the plugin/UI and can be tested separately.
Pushing a v*.*.* tag builds all supported platforms and produces:
RP2A03_synth/
├── Windows/ # x86_64, OpenGL
├── Linux/ # x86_64, wgpu/Vulkan
└── Mac/ # x86_64 + arm64, wgpu/Metal
A manual GitHub Actions run also produces the build artifact without creating a release.
If the plugin crashes or hangs your DAW, attach the crash log to the issue.
| Platform | Crash log |
|---|---|
| Windows | %LOCALAPPDATA%\rp2a03_synth\crash.log |
| macOS | ~/Library/Application Support/rp2a03_synth/crash.log |
| Linux | $XDG_DATA_HOME/rp2a03_synth/crash.log |
Set NICE_LOG to override the log location.
| Feature | Status |
|---|---|
| 2A03 Pulse | ✅ Done |
| 2A03 Triangle | ✅ Done |
| 2A03 Noise | ✅ Done |
| VRC6 Pulse | ✅ Done |
| VRC6 Sawtooth | ✅ Done |
| Sunsoft 5B PSG | ✅ Done |
| FDS Wavetable | Planned |
| Namco 163 | Planned |
| 2A03 DPCM | Not planned |
This project builds on research and open-source work from:
- TetaNES — 2A03/APU architecture
- MesenCE — VRC6 implementation reference
- emu2149 — Sunsoft 5B PSG
- Furnace — AY8930 duty-cycle reference
- FamiStudio
- Dn-FamiTracker
- puNES
- NesDEV Wiki
See the upstream projects for their respective licenses.
Original code written specifically for this project is released under the WTFPL v2.
Adapted code remains under its original upstream license.
Development has used Anthropic Claude Code.
