Open hardware · CERN-OHL-S v2 · Release coming soon
NOAIDI

A 32-voice polysynth, entirely in gateware.

Noaidi is a virtual analog synthesizer whose sound engine runs on a $25 FPGA board. 256 oscillator-filter partials, computed every sample at 96 kHz. No DSP chip and no operating system in the audio path.

Source: github.com/Thorwegian/noaidi-fpga-synth

32voices, 8 partials each
256partials per sample
96 kHzinternal sample rate
2×state-variable filters per partial
IDLE · 7+1 SUPERSAW · MODEL96 kHz · Q4.14
Recorded from the hardware

Eight takes from the development bench.

Recorded straight from the board, unedited.

GalaxyFactory pad, 4+4 pulse structure · D major
1:00
DramaticChords with the mod envelope on cutoff · F major
0:36
MoogyLead patch · F minor
0:37
ChariotsKeys patch, played by hand · F major
0:33
CanonSustained polyphony · C major
0:41
Bass soloBass patch · C minor
0:28
ResonanceFilter resonance sweeps · F minor
0:29
BandpassThe filter in bandpass mode · A minor
0:47
Play it in your browser

The two factory pads, modelled in your browser.

The demo runs the same patch values as the instrument, so you hear its character rather than a recording of it. Turn the knobs; they map to the same MIDI controls.

Browser model. An AudioWorklet re-implements the engine's arithmetic: free-running oscillators, two state-variable filter sections, log-domain envelopes and LFOs, at 96 kHz where the device allows. Expect the character, not the exact tone. Headphones help.
Pads / Factory
Keys A…K play white notes, W E T Y U black. Z / X shift octave. MIDI: checking…
Two chips, one job each

The FPGA computes sound. The ESP32 decides what to play.

An ESP32-C3 reads MIDI, allocates voices and maps every knob to engine parameters. It writes those parameters over SPI to a Tang Nano 20K, where a single time-multiplexed datapath computes all 256 partials once per sample. Every musical decision is made in firmware; the FPGA only computes samples.

MIDI DIN 5-pin, opto 31250 bd ESP32-C3 · CONTROL MIDI parser · event bus voice allocator, 32 voices CC → parameter mapping patches: TOML on 4 MB flash bank / program change USB serial console 1 kHz tick · at most 1 ms latency SPI 40 MHz parameter pages TANG NANO 20K · SOUND GW2AR-18C FPGA, 73.728 MHz 256-partial pipeline, 26 stages CSP: control program, 48 kHz passes 2 × SVF per partial, 12 / 24 dB log stereo attenuation · mix sat24 limiter · output LPF 768 clocks per sample S/PDIF48 kHz optical I2S96 kHz DAC
Control plane in blue, audio in amber. The parameter image is double-buffered and swapped at sample boundaries, so a knob turn never lands mid-sample.
DMEM · 1024 WORDS · Q8.10 effective = base + dmem[pointer], saturating · pitch · duty · cutoff · 1/Q · gain L · gain R OSCsaw pulse tri sineUQ0.24 phase SVF 1TPT / ZDFLP · BP · HP SVF 2cascade → 24 dBQ8.28 states L / R GAINattenuationset in dB MIX26-bit accsat24 limiter × 256 partials per sample · parameters arrive from the control processor through data memory
256 partials per sample through a 26-stage pipeline.
Full-range modulation

The mod envelope can move cutoff by ±16 octaves and the cutoff control by ±8, so any destination can be driven end to end.

A control processor of its own

A small pipelined processor on the FPGA runs the envelopes, LFOs and modulation routings as one straight-line program, about 1,470 cycles per pass at 48 kHz, and writes the results into the partials' parameter memory.

Specifications

Voice

Polyphony32 voices, 256 partials
Structures1+1, 7+1, 4+4 partials per voice
Oscillators2 per voice: saw, pulse, triangle, sine
TuningCoarse ±12 st, fine ±0.5 st, mix, pulse width 50 → 5 %
UnisonDetune per step, stereo spread to hard pan
Pitch resolutionlog₂ UQ4.10, about 1.17 cents

Filter

Topology2 × TPT / ZDF state-variable, per partial
ModesLowpass, bandpass, highpass · 12 or 24 dB/oct
Cutoff±8 octaves, 14-bit with CC 74 + 106
ResonanceButterworth to Q ≈ 32, self-oscillation reachable
Key tracking0 → 200 %, follows bend and LFO pitch

Modulation

EnvelopesAmp and mod ADSR per voice, in gateware, velocity amounts
LFOs2 · 0.03 → 30 Hz · saw, pulse, tri, sine → PW, resonance, pitch, cutoff
Mod envelopeBipolar, square-law, ±16 octaves to cutoff
ProcessorCSP: pipelined control processor, 2048-row program memory, one pass per 48 kHz period
PerformanceMod wheel, bend (RPN range 1–12 st), sustain, omni and per-channel multitimbral

Audio

Engine rate96 kHz · 768 clocks per sample
TransportQ4.14 audio, Q8.28 filter states
OutputsS/PDIF 48 kHz (primary), I2S 96 kHz
Limiter26-bit mix accumulator, sat24

Hardware

EngineSipeed Tang Nano 20K, Gowin GW2AR-18C, 20,736 LUT4
Clock73.728 MHz from the on-board MS5351
ControlESP32-C3 Super Mini, 4 MB flash, ESP-IDF v6
LinkSPI mode 0, 40 MHz, 16-bit address / 32-bit data
MIDIDIN in via optocoupler, second panel port
StorageFAT partition, TOML patches, banks.toml

Project

GatewareSystemVerilog, Yosys + nextpnr, Verilator 5 testbenches
FirmwareC on ESP-IDF, host-side unit tests
LicenseCERN-OHL-S v2, strongly reciprocal
AuthorThor H. Linløkken, Norway
Patches are text

Your sounds are files you can read, diff and share.

Each patch is one TOML file on a FAT partition in the ESP32's flash. Open it in any editor or keep it in git. The firmware clamps out-of-range values, ignores keys it does not know, and never formats your partition on its own.

  • Real units. Cutoff in hertz at C4, envelope times in seconds, sustain in dB, detune in semitones. Not 0–127 controller values.
  • Banks map to MIDI. A banks.toml lists which patch answers each Bank Select and Program Change, numbered 0 to 16383.
  • Safe saves. The synth writes a temporary file first and swaps it in, so a power cut never corrupts the old patch. Factory patches are never overwritten.
  • Sync from the shell. make fw-storage-read pulls the whole library to a folder on your computer. make fw-storage-write pushes it back.
# Patches/Factory/Pads/Supersaw.toml
format = 1

[voice]
structure = "7+1"
osc_mix = 0
unison_detune = 0.07      # semitones per step
unison_stereo = 100

[[osc]]
wave = "saw"
tune = 0

[[osc]]
wave = "sine"
tune = -12                 # sub, one octave down

[filter]
type = "lowpass"
slope = 24
cutoff = 370               # Hz at C4
resonance = 1
key_track = 100

[amp_env]
attack = 0.042
decay = 3
sustain = -3.76            # dB
release = 11
velocity = 50

[mod_env]
destination = "cutoff"
depth = 24                 # semitones
sustain = 93.75

[[lfo]]
shape = "triangle"
rate = 1
destination = "pitch"
depth = 0.094

[part]
bend_range = 2
Build one

Two off-the-shelf boards and an open-source toolchain.

No vendor licence or proprietary bitstream tools. The gateware builds with Yosys and nextpnr, simulates in Verilator, and loads with openFPGALoader. The firmware is plain ESP-IDF.

PartRoleStreet price
Sipeed Tang Nano 20KSound engine, GW2AR-18C FPGA≈ $25–35
ESP32-C3 Super MiniMIDI, voices, patches, console≈ $3–8
6N138 optocoupler + DIN socketMIDI input≈ $2
TOSLINK transmitter or I2S DACAudio out, optical or analog≈ $3–10

Prices are typical listings in late 2026 and vary by seller and shipping. The reference wiring needs no custom board.

Toolchain

yosysnextpnr-himbaechelgowin_packopenFPGALoaderVerilator 5ESP-IDF v6Python 3

Build steps are in the repository README.

Questions

Why an FPGA?

Cost. A $25 FPGA board gives 32 voices, each with eight oscillators and two filters, at 96 kHz. Commercial polysynths with a quarter of that cost several hundred to a few thousand dollars.

How much of the FPGA does it use?

On the GW2AR-18C: 44 % of the LUTs, 77 % of the flip-flops and 43 of the 46 block RAMs. Block RAM is the tight one.

What is the latency?

Parameter changes reach the engine on a 1 kHz tick, so at most 1 ms after the MIDI byte is parsed. The audio pipeline is 26 clock stages at 73.728 MHz, well under one sample.

Why 96 kHz inside and 48 kHz out?

The engine runs at 96 kHz and decimates by two for the S/PDIF output, which is the primary path. The I2S output carries the full 96 kHz stream for a DAC.

Are the oscillators band-limited?

No, and they need not be. The engine runs at 96 kHz, so the aliasing of a plain saw or pulse lands above the hearing range, and the decimation to 48 kHz removes it before the output. That is what the 96 kHz is for.

Does it work with any MIDI controller?

Yes. It takes 5-pin DIN MIDI and uses standard controller numbers, so any keyboard or sequencer with a MIDI out drives it with no software in between.

Can I buy one?

Not yet. Today you build it from a Tang Nano 20K, an ESP32-C3 and a few parts, following the repository. A release build is the next milestone.

Why TOML for patches?

So a patch is readable without the instrument: real units, comments allowed, diffs that make sense in git.

Where it is going

Playable today from source. A release build is next.

Layers and key splitsStack or divide timbres across the keyboard.
Arpeggiator and step sequencerClock-synced, with the control numbers already reserved.
EffectsThe engine has spare cycles for them.
More waveformsNoise, oscillator drift and a parabolic wave.
Parameter smoothingNo zipper noise from fast wheel and knob moves.
LFO routing matrixBoth LFOs to any of eight destinations, tremolo included.
Patch editorA companion app for editing patches from a computer.
SysExDirect parameter access for editors and sequencers.

Follow the release.

Watch the repository for releases, read the design documents, or clone it and build one now.

Star on GitHub Design document