Browse by type
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.
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
picotool for UF2 conversionYou will need:
- rustup
- Rust (1.89 or newer) with thumbv8m.main-none-eabihf target (rustup target add thumbv8m.main-none-eabihf)
- picotool
cd faderpunk # important, not in root
cargo build --release
Use the provided script to build and convert to UF2 format:
# this needs to be done in the repository root
./build-uf2.sh
This creates target/thumbv8m.main-none-eabihf/release/faderpunk.uf2
faderpunk.uf2 to the deviceFor rapid development with debug output:
cd faderpunk
cargo build
# Use probe-rs or similar tool for flashing with RTT debug output
The Faderpunk Configurator is a React/TypeScript web application that communicates with the device via WebUSB.
You will need: - NodeJS v22.x or higher - pnpm
Before building the configurator you'll need to run
# in the root
./gen-bindings.sh
This will create the Postcard bindings for the configurator (from libfp).
cd configurator
pnpm install
pnpm dev
Access 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
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::Mid);
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));
}
}
faderpunk/src/apps/mod.rs 42 => my_app,
# Check compilation
cargo check
# Run Clippy linter
cargo clippy
# Format code
cargo fmt
The 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:
Apps implement scene save/load by serializing their state with postcard.
midly crateThe workspace Cargo.toml configures aggressive optimization:
[profile.release]
lto = true
incremental = false
codegen-units = 1
debug = 2
This maximizes performance while retaining debug symbols for profiling.
Contributions are welcome! Please follow the Rust Code of Conduct.
cargo fmt and cargo clippy before committingFaderpunk uses a dual-track release system managed by release-please:
- Beta releases: Published from the develop branch (e.g., 1.6.0-beta.5)
- Stable releases: Published from the main branch (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:
bash
git checkout develop
git merge feature-branch
git push origin develop
Release-please creates a PR:
Review the PR to verify version bumps and changelog
Merge the release PR:
prerelease: true flag/beta pathStable releases happen when develop is ready for production:
Create PR from develop to main:
bash
git checkout develop
git push origin develop # Ensure develop is up to date
Then create a PR on GitHub from develop → main
Review and merge to main:
Merge to main when ready for stable release
Release-please creates a release PR on main:
Version numbers will match what was in develop
Merge the release PR:
libfp is 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:
bash
git checkout main
git pull origin main
git checkout -b hotfix/critical-bug
Make your fix and push:
bash
# Make your changes
git add .
git commit -m "fix: critical bug in X"
git push origin hotfix/critical-bug
Create PR to main and merge:
mainReview and merge the PR
Release-please creates a patch release PR:
1.5.0 → 1.5.1)This triggers the build and publish workflow
Sync the fix back to develop:
bash
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.
main back into develop:
bash
git checkout develop
git pull origin develop
git merge main --no-edit
git push origin developThis ensures release-please on develop sees the stable release commits and doesn't get confused about what's been released.
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.0 and configurator-1.6.1, beta should jump to 1.6.0-beta.0 and 1.7.0-beta.0:
```bash # 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
$ claude mcp add faderpunk \
-- python -m otcore.mcp_server <graph>