A powerful, modular eurorack and MIDI synthesizer controller built on the RP2350B microcontroller. Faderpunk provides 16 channels of flexible, programmable control with faders, buttons, CV jacks, and full MIDI integration, all configured through an intuitive web interface.
Faderpunk is an embedded Rust project that uses an RP2350B to create a feature-rich eurorack and MIDI controller. Each of the 16 channels can run a different "app" - from LFOs and sequencers to MIDI converters and Turing machines - creating a highly versatile control surface for modular synthesis.
- 16 Independent Channels: Each channel features a fader, button, RGB LED, and configurable CV jack
- Modular App Architecture: Run different apps on different channels simultaneously
- Dual-Core Performance: Hardware tasks on Core 0, application logic on Core 1
- WebUSB Configuration: Browser-based configurator with drag-and-drop layout management
- FRAM Storage: Persistent scene storage with fast save/recall
- Full MIDI Support: USB MIDI device capabilities
- I2C Integration: Compatible with 16n faderbank protocol
- Real-time Control: Async architecture ensures responsive performance
- RP2350B (Raspberry Pi Pico 2)
- Dual Cortex-M33 cores @ 150 MHz (overclocked to 250MHz)
- 520 KB SRAM
- MAX11300: 20-port programmable mixed-signal I/O (ADC/DAC)
- FM24V10: 1 Mbit FRAM for persistent storage
- WS2812B: RGB LED chains for visual feedback
- 16 faders, 16 buttons, configurable CV jacks per channel
Faderpunk uses a sophisticated dual-core architecture to maximize performance:
Runs dedicated Embassy async tasks for hardware interfaces:
- MAX11300 ADC/DAC communication
- Button scanning and debouncing
- LED control (WS2812B)
- MIDI input/output
- FRAM storage operations
- I2C communication
- WebUSB protocol handling
Executes user-facing apps:
- Each app runs as an independent Embassy task
- Apps receive hardware events via PubSub channels
- Apps send commands to hardware via async channels
- Clean abstraction through the
App<N>API
- Event PubSub: Broadcasts input events (button presses, fader changes, MIDI) to all apps
- Command Channel: Apps send hardware commands (set LED color, configure CV, send MIDI)
- Watch Channels: Global configuration and clock synchronization
- Serialization: Postcard format for efficient no_std data exchange
- Raspberry Pi Pico 2 (RP2350B)
- Faderpunk PCB with supporting components
- USB cable for programming and power
- Rust toolchain (nightly recommended)
picotoolfor UF2 conversion- Chromium-based browser for WebUSB configurator
You will need:
rustup- Rust (1.89 or newer) with
thumbv8m.main-none-eabihftarget (rustup target add thumbv8m.main-none-eabihf) - picotool
cd faderpunk # important, not in root
cargo build --releaseUse the provided script to build and convert to UF2 format:
# this needs to be done in the repository root
./build-uf2.shThis creates target/thumbv8m.main-none-eabihf/release/faderpunk.uf2
- Hold the SHIFT button (the one on the very bottom right, the yellow one) while connecting Faderpunk to your computer via USB.
- Device appears as a mass storage device
- Copy
faderpunk.uf2to the device - Device automatically reboots with new firmware
For rapid development with debug output:
cd faderpunk
cargo build
# Use probe-rs or similar tool for flashing with RTT debug outputThe Faderpunk Configurator is a React/TypeScript web application that communicates with the device via WebUSB.
- Drag-and-drop layout management
- Real-time parameter configuration
- Global settings (MIDI, I2C, clock, quantizer)
- Scene management
- Live visual feedback
You will need:
Before building the configurator you'll need to run
# in the root
./gen-bindings.shThis will create the Postcard bindings for the configurator (from libfp).
cd configurator
pnpm install
pnpm devAccess at http://localhost:5173
NOTE: When changes to libfp happen, the bindings will need to be regenerated. Delete the node_modules folder in configurator and do the following steps in that case.
WebUSB requires:
- Chrome/Chromium
- Edge
- Opera
- Brave
- Vivaldi or any Chromium-based browser
Firefox and Safari do not support WebUSB.
For more details, see configurator/README.md
faderpunk/
├── faderpunk/ # Main firmware crate
│ ├── src/
│ │ ├── main.rs # System initialization, core orchestration
│ │ ├── app.rs # App API and abstractions
│ │ ├── apps/ # App implementations
│ │ ├── tasks/ # Hardware driver tasks
│ │ ├── events.rs # Event types
│ │ ├── storage.rs # FRAM persistence
│ │ └── layout.rs # Channel layout management
│ └── Cargo.toml
├── libfp/ # Shared library
│ ├── src/ # Common types and utilities
│ └── Cargo.toml
├── configurator/ # Web configurator
│ ├── src/
│ └── package.json
├── gen-bindings/ # TypeScript binding generator
└── Cargo.toml # Workspace configuration
- Create a new file in
faderpunk/src/apps/my_app.rs:
use embassy_futures::select::select;
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, signal::Signal};
use libfp::{AppIcon, Brightness, Color, Config, Range};
use crate::app::{App, Led};
pub const CHANNELS: usize = 1;
// App configuration visible to the configurator
pub static CONFIG: Config<0> = Config::new(
"My App",
"Description of what this app does",
Color::Blue,
AppIcon::Fader,
);
// Wrapper task - required for all apps
#[embassy_executor::task(pool_size = 16/CHANNELS)]
pub async fn wrapper(app: App<CHANNELS>, exit_signal: &'static Signal<NoopRawMutex, bool>) {
// Exit handler allows clean shutdown when app is removed from layout
select(run(&app), app.exit_handler(exit_signal)).await;
}
// Main app logic
pub async fn run(app: &App<CHANNELS>) {
let output = app.make_out_jack(0, Range::_0_10V).await;
let fader = app.use_faders();
let buttons = app.use_buttons();
let leds = app.use_leds();
leds.set(0, Led::Button, Color::Blue, Brightness::Lower);
loop {
buttons.wait_for_down(0).await;
let value = fader.get_value();
output.set_value(value);
leds.set(0, Led::Top, Color::Blue, Brightness::Custom((value / 16) as u8));
}
}- Register in
faderpunk/src/apps/mod.rs
42 => my_app,- Build the firmware and see if you can find your app in the configurator
# Check compilation
cargo check
# Run Clippy linter
cargo clippy
# Format code
cargo fmtThe project uses defmt for structured logging:
defmt::info!("Button pressed on channel {}", channel);View logs using probe-rs or similar RTT-capable debugger.
Faderpunk uses a 1 Mbit FRAM (FM24V10) for persistent storage:
- Fast writes (no wear leveling needed)
- Scene-based storage system
- Apps can save/load state via serialization
- Global configuration persistence
Apps implement scene save/load by serializing their state with postcard.
- USB MIDI device (shows as MIDI interface to host)
- Configurable channel routing
- Full MIDI message support via
midlycrate
- 16n faderbank protocol support
- Eurorack module integration
- Configurable addressing
- COBS framing for reliable packet transmission
- Postcard serialization for compact binary protocol
- Type-safe message definitions shared between firmware and configurator
- Real-time bidirectional communication
The workspace Cargo.toml configures aggressive optimization:
[profile.release]
lto = true
incremental = false
codegen-units = 1
debug = 2This maximizes performance while retaining debug symbols for profiling.
Contributions are welcome! Please follow the Rust Code of Conduct.
- Use
cargo fmtandcargo clippybefore committing - Test on hardware when possible
- Document new apps and features
- Update TypeScript bindings when changing protocol types
- Fork the repository
- Create a feature branch
- Make your changes with clear commit messages
- Ensure code passes clippy and builds successfully (warnings are, for the most part ok and expected at this point)
- Test on hardware if applicable
- Submit a pull request with a clear description
Faderpunk uses a dual-track release system managed by release-please:
- Beta releases: Published from the
developbranch (e.g.,1.6.0-beta.5) - Stable releases: Published from the
mainbranch (e.g.,1.5.0)
Both workflows are automated via GitHub Actions, but version management requires manual steps to keep the tracks synchronized.
Beta releases happen automatically when commits are merged to the develop branch:
-
Merge changes to
develop:git checkout develop git merge feature-branch git push origin develop
-
Release-please creates a PR:
- Workflow runs automatically on push
- Creates/updates a release PR with changelog
- Review the PR to verify version bumps and changelog
-
Merge the release PR:
- Merge the release-please PR on GitHub
- This triggers the build and publish workflow
- Beta releases are published with
prerelease: trueflag - Configurator deploys to GitHub Pages at
/betapath
Stable releases happen when develop is ready for production:
-
Create PR from
developtomain:git checkout develop git push origin develop # Ensure develop is up to dateThen create a PR on GitHub from
develop→main -
Review and merge to
main:- Review the PR carefully
- Merge to
mainwhen ready for stable release
-
Release-please creates a release PR on
main:- Workflow runs automatically
- Creates/updates a release PR with changelog
- Version numbers will match what was in develop
-
Merge the release PR:
- Merge the release-please PR on GitHub
- This triggers the build and publish workflow
- Stable releases are published as full releases (not prereleases)
- Configurator deploys to GitHub Pages root path
libfpis published to crates.io (if version changed)
Patch releases allow you to fix critical bugs in stable releases without pulling in new features from develop.
For example, to release 1.5.1 while develop is working on 1.6.0-beta.x:
-
Create a hotfix branch from
main:git checkout main git pull origin main git checkout -b hotfix/critical-bug
-
Make your fix and push:
# Make your changes git add . git commit -m "fix: critical bug in X" git push origin hotfix/critical-bug
-
Create PR to
mainand merge:- Create a PR from your hotfix branch →
main - Review and merge the PR
- Create a PR from your hotfix branch →
-
Release-please creates a patch release PR:
- Workflow runs automatically
- Creates/updates a release PR (e.g.,
1.5.0→1.5.1) - Review and merge the release PR
- This triggers the build and publish workflow
-
Sync the fix back to
develop:git checkout develop git pull origin develop git merge main --no-edit git push origin develop
This ensures the fix is included in future beta and stable releases.
Important: You don't need to bump beta versions after a patch release, since beta is already ahead on the minor version (e.g., 1.6.0-beta.x is ahead of 1.5.1).
IMPORTANT: After a stable release is published, you must sync the release history back to develop and bump beta versions ahead of stable.
-
Merge
mainback intodevelop:git checkout develop git pull origin develop git merge main --no-edit git push origin develop
This ensures release-please on
developsees the stable release commits and doesn't get confused about what's been released. -
Bump beta versions ahead of stable using Release-As:
Use release-please's
Release-As:footer to manually set the next beta version. You need to create separate commits for each package, touching a file in each package directory so release-please correctly assigns the version.For example, if stable just released
faderpunk-1.5.0andconfigurator-1.6.1, beta should jump to1.6.0-beta.0and1.7.0-beta.0:# Bump faderpunk to next beta version touch faderpunk/.release-as git add faderpunk/.release-as git commit -m "chore(faderpunk): bump to next beta version" -m "Release-As: 1.6.0-beta.0" # Bump configurator to next beta version touch configurator/.release-as git add configurator/.release-as git commit -m "chore(configurator): bump to next beta version" -m "Release-As: 1.7.0-beta.0" # Push to trigger release-please git push origin develop
The
Release-As:footer tells release-please to use that specific version for the next release, preventing it from bundling old commits. -
Review and merge the release PR:
- Release-please will create/update a PR with the new beta versions
- Review the PR to ensure versions are correct
- Merge the PR to publish the new beta releases
- Beta versions must always be ahead of the latest stable release
- Use semantic versioning:
MAJOR.MINOR.PATCHfor stable,MAJOR.MINOR.PATCH-beta.Nfor beta - When stable releases
X.Y.0, beta should jump toX.(Y+1).0-beta.0 - The merge from
maintodevelopis required for release-please to track what's been released - Use
Release-As:commit footer to manually set beta versions after stable releases - Always touch a file in the package directory when using
Release-As:so release-please assigns the version correctly
Each release produces:
Firmware (faderpunk/):
faderpunk.elf- ELF binary for debuggingfaderpunk-vX.Y.Z.uf2- UF2 file for flashing to device
Configurator (configurator/):
configurator.zip- Downloadable web app bundle- GitHub Pages deployment (root for stable,
/betafor beta)
Library (libfp/ - stable only):
- Published to crates.io when version changes
This project is licensed under GNU General Public License v3.0 (GPL-3.0).
See LICENSE for full terms.
Faderpunk is developed by ATOV
- UI/UX by Leise St. Clair
- Icon design by papernoise
- Community: Discord Server
- Issues: GitHub Issues
- Website: atov.de
- Dual-core async execution
- Sub-millisecond event response
- 12-bit CV resolution
- USB 1.1 (device and host)
- I2C (configurable speed)
- MIDI (USB and Serial)
- USB-powered
- Eurorack power compatible
Built with embedded Rust, Embassy async runtime, and a passion for modular synthesis.