A Serum-style wavetable synthesizer plugin — VST3 · AU · Standalone for macOS, Windows and Linux. Two morphing wavetable oscillators, a dual zero-delay filter, deep modulation and a full FX rack, with a live 3D view of the wavetable terrain. Built in C++ on JUCE.
Grab the zip for your platform from the latest release, then drop the plugin in the right folder:
| Platform | VST3 | AU | Standalone |
|---|---|---|---|
| macOS | ~/Library/Audio/Plug-Ins/VST3/ |
~/Library/Audio/Plug-Ins/Components/ |
drag the .app to Applications |
| Windows | C:\Program Files\Common Files\VST3\ |
— | run the .exe |
| Linux | ~/.vst3/ |
— | run the binary |
The plugins ship as FableSynth WT-1 (wavetable synth), FableSynth DR-1 (drum machine), FableSynth BL-1 (acid bassline) and FableSynth SQ-4 (session launcher) — separate zips per platform.
Then rescan plugins in your DAW. macOS builds are universal (Apple Silicon +
Intel) and ad-hoc signed; on first launch you may need to right-click → Open, or
clear quarantine with xattr -dr com.apple.quarantine <path>.
Building from source instead? See
juce/README.md.
-
2 wavetable oscillators — 6 procedural tables (PRIME, BLOOM, PULSE, VOX, CHIME, GLITCH), each 16 morphable frames × 2048 samples. Tables are built from harmonic spectra and rendered into 9 band-limited mip levels via inverse FFT, so high notes never alias (the same trick Serum uses). Mip transitions are crossfaded inside the 0.475·sr guard band, so glides and pitch bends never step in brightness — worst measured non-harmonic content is −92 dB:
-
User wavetables — import an audio file (single-cycle, autocorrelation pitch auto-detect, or fixed cycle length — sliced into 2048-sample frames with resampling) or draw a single cycle. Every imported/drawn frame runs through the same FFT band-limit + 9-level mip pipeline as the procedural tables (
buildUserTable), so they anti-alias identically. The import/draw editor (the ✎ button on each oscillator) is available in both the web app and the plugin; in the VST, user tables are saved with the plugin/DAW project state. -
Unison up to 7 voices per oscillator with detune + stereo spread, octave/semi/fine tuning, level and pan per oscillator.
-
Sub oscillator (sine / polyblep square, −1/−2 oct) and noise (white/pink).
-
Dual per-voice filter — two independent filters, each offering the Simper (Cytomic) zero-delay state-variable types (LP12, LP24, BP, HP, Notch) plus a tuned comb (CUTOFF sets pitch, RES sets feedback) and a vowel/formant filter (CUTOFF morphs A-E-I-O-U), all with envelope amount and key tracking. They route serial (F1→F2), parallel (summed) or split (osc A→F1, osc B→F2). The
tanhdrive is anti-aliased (first-order antiderivative anti-aliasing, ADAA), so pushing DRIVE on bright material doesn't fold harmonics back down the way a naive per-sample saturator would. -
2 ADSR envelopes (amp + mod), 2 LFOs (5 shapes) and Serum-style mod assignment — drag any source (LFOs / mod env / velocity / note) straight onto a knob or the wavetable POS slider to create a route, then drag the colored ring it grows to set the depth. Each control can carry several routes, and the mod matrix lists every connection (including the global pitch / amp / pan targets) for direct editing. Destinations: wavetable position, F1/F2 cutoff, F2 resonance, pitch, amp, pan, osc levels.
-
8-voice polyphony with smart voice stealing, glide, pitch-bend, and a per-voice DC blocker so saturation stages never see offset.
-
FX chain — tanh drive, stereo chorus, ping-pong delay, reverb, safety limiter.
-
Live 3D wavetable views show the actual modulated frame position streamed back from the DSP thread, plus oscilloscope, spectrum analyser and filter response displays.
The live 3D wavetable terrain for both oscillators (the highlighted line is the frame currently playing, tracked from the DSP thread):
FableSynth started as a synth that runs entirely in the browser — the oscillators, filters and modulation are an AudioWorklet DSP core in TypeScript, the rack is React + Zustand with custom dependency-free UI. The plugin is a faithful C++/JUCE port of that engine: the DSP is reimplemented one-to-one as JUCE-independent pure C++, with the same parameters, the same 20 factory presets and the same signal flow, so a patch sounds the same in either.
The web app is still the place to import and draw user wavetables, and it's the quickest way to try the synth with nothing to install:
fablesynth.mp4
cd fablesynth
npm install
npm run dev
# open the printed http://localhost:5173npm run build produces a static bundle in dist/ (npm run preview serves it).
AudioWorklet requires a real HTTP origin (not file://), which the dev/preview
servers provide. Click the power button (also the browser audio-unlock gesture)
and play.
| Input | Action |
|---|---|
| Knobs | drag vertically · shift = fine · double-click = reset · scroll wheel |
| Computer keys | A W S E D F T G Y H U J K O L P ; ' play notes · Z/X octave |
Esc |
panic (all notes off, stops the sequencer) |
| MIDI | plug in a controller — notes + pitch bend (Chrome/Edge) |
| On-screen keys | click/touch, vertical position = velocity, drag for glissando |
The web rack also carries a 16-step note sequencer (the NOTE SEQ panel,
web-only for now): 12 note lanes per step with per-step octave (−1/0/+1),
accents (full velocity — route VELO in the mod matrix to make them bite)
and ties (legato retune of the sounding voice, no envelope retrigger; turn
up GLIDE and a tie becomes a slide). Four patterns A–D with chaining, swing,
gate length, a root-note stepper and RAND — the same workflow as the DR-1 and
BL-1 sequencers, driving the full polyphonic engine. Synced LFOs lock to the
sequencer tempo, and patterns persist in localStorage. This is the WT-1
half of the groundwork for the FableSeq SQ-4 session launcher.
DR-1 is a 16-pad drum machine built on the same wavetable engine. Each pad combines a morphing wavetable oscillator with a true one-shot sample player, plus noise, modulation and shaping. It comes in two forms:
- Plugin — FableSynth DR-1 (VST3 · AU · Standalone), a faithful
C++/JUCE port built from the same
juce/project as WT-1, adding real 5-bus multi-out (MAIN + AUX 1–4 as separate DAW mixer channels), host tempo sync, and drop-WAV pad import saved with the project state. Seejuce/README.md. - Web app — served at
/drum/(npm run dev, then openhttp://localhost:5173/drum/).
The web build:
- 16 pads, each with a wavetable oscillator (4 drum tables — THUD, CRACK, TINE, GRIT — plus WT-1's six procedural tables, with the same POS morphing and up to 7-voice unison), a tunable 32-voice one-shot sample player with the CC0 808 and public-domain UZU banks, and a noise layer with color control.
- Per-pad shaping — pitch envelope (±48 st), AHD amp envelope with adjustable curve, a switchable filter (LP12/LP24/BP/HP/Notch) with drive, and a 4-slot mod matrix (MOD ENV / VELO / RAND → wavetable pos, pitch, cutoff, levels …). Velocity→level and velocity→mod amounts per pad.
- Drop a WAV on a pad and it's sliced into a wavetable through the same band-limit pipeline as WT-1 user tables — any sample becomes a synthesized, retunable drum.
- 16-step sequencer — tap steps through off → on → accent, 4 patterns (A–D) per kit with pattern chaining, swing, and 60–200 BPM.
- Choke groups (4) so open/closed hats cut each other, and per-pad output routing (MAIN + 4 aux buses).
- Master FX — drive, bus compressor, chorus, delay, reverb.
- Kits — 14 factory kits, including sample-based 808 CLASSIC and UZU plus
the oscillator/sample-blended 808+UZU HYBRID;
saved kits persist in
localStorage, including patterns, chains and any dropped-in wavetables. - Pad patches and SQ-4 content — 36 pad patches include 16
HXhybrids with both oscillator and sample layers active; 32 DR-1 clips and the six factory scenes are voiced around the 808, UZU and blended kits. - Play it — pads map to
Z X C V / A S D F / Q W E R / 1 2 3 4(bottom row = pads 1–4),Escstops, and MIDI notes 36–51 (C1 up) trigger pads 1–16.
BL-1 is a monophonic acid bassline machine built on the same wavetable engine — one last-note-priority voice with the classic 303 interplay of accents (one knob boosts level, filter-env peak and shortens its decay) and slides (tied steps glide the pitch while both envelopes keep running). Two builds from one source of truth:
- Web app — served at
/bass/(npm run dev, then openhttp://localhost:5173/bass/). - Plugin — FableSynth BL-1 (VST3 · AU · Standalone), a faithful
C++/JUCE port of the same engine with host tempo sync and transport lock
(the DAW playhead drives the pitch sequencer sample-accurately), MIDI
audition of the mono voice, and the factory patches as host programs. Ships
from the same build as WT-1/DR-1 — see
juce/README.md.
- One wavetable oscillator (WT-1's six procedural tables with POS morphing and up to 7-voice unison with stereo spread) over a sub oscillator (sine / polyblep square, −1/−2 oct).
- Zero-delay SVF filter (LP12/LP24/BP/HP/Notch, LP 24 default) with ADAA drive, resonance, key tracking and a snappy AD filter envelope (±5 octaves of sweep); amp is a full ADSR.
- Bar-locked LFO → cutoff — synced divisions (1/1 … 1/32, dotted & triplet), 5 shapes, phase locked to the sequencer transport.
- 16-step pitch sequencer — tap one of 12 note lanes per step, per-step octave (−1/0/+1), accent and slide rows, glowing slide connectors, 4 patterns (A–D) with chaining, swing, RAND pattern generator, 60–200 BPM.
- FX — post-accent drive, chorus, ping-pong delay, reverb. Deliberately no bus compressor: accents stay live.
- Patches — 3 factory patches (ACID LINE, RUBBER SUB, NEON SQUELCH);
saved patches (sound + patterns + chain) persist in
localStorage. - Play it — two-octave on-screen keyboard (audition when stopped,
overlapping keys = legato slide), computer keys
A W S E D …withZ/Xoctave,Escstops, MIDI in from C2 with last-note priority.
SQ-4 is a session clip launcher in the spirit of Ableton's Session View,
driving all three real instruments from one page: one shared
AudioContext hosts a DR-1, a BL-1 and two WT-1 engines (LEAD + PADS), four
tracks crossed with six scenes of real, playable clips. Tap a clip to launch
it, tap again to stop; scenes layer freely and the latest launch wins each
track. Launches quantize to the clock (1 BAR, 1/4 or OFF): the
conductor stamps every command with an absolute context frame, and because
all four worklets share the same currentFrame timebase, clips launched at
one boundary start in the same render quantum and never drift apart. Queued
cells pulse until the boundary; the grid flips to LIVE on the worklet's ack —
exactly when the audio changed. Per-track mute/solo and per-scene mute are
gain ramps on the shared graph; pause is ctx.suspend(), so everything
freezes and resumes in phase. The SUM scope and the VU meters draw the real
per-track analysers. The factory session (NEON TALE, 122 BPM) ships six
scenes of handcrafted patterns in each machine's native format. State,
document schema and the hosted worklet protocol are specified in
docs/sq4-clips.md.
- Web app — served at
/seq/(npm run dev, then openhttp://localhost:5173/seq/).
The plugins (C++/JUCE — WT-1, DR-1 and BL-1) live in juce/ — see
juce/README.md for the DSP/UI source maps and the headless
verification harnesses.
The web prototype (TypeScript):
src/engine/worklet.js DSP core (AudioWorklet thread): voices, oscillators,
filter, envelopes, LFOs, mod matrix
src/engine/wavetables.ts FFT + procedural wavetable/mipmap generation
(+ buildUserTable: shared band-limit for user tables)
src/engine/usertables.ts user wavetables: audio import (resample, autocorr
cycle detect), draw mode, base64-Float32 (de)serialize
src/engine/synth.ts AudioContext, FX graph, param routing
src/components/WavetableEditor.tsx import / draw modal
src/params.ts single source of truth for every parameter
src/presets.ts factory + localStorage user presets
src/noteseq.ts 16-step note-sequencer data model + timing math
src/store.ts Zustand store: param state + transport, engine glue
src/components/ knobs, steppers, sliders, keyboard, canvas displays
src/components/panels/ the rack layout (oscillators, filter, env, fx, …)
src/hooks/ computer-keyboard + MIDI input
src/App.tsx top-level composition
The DR-1 drum machine (TypeScript, third vite entry at drum/index.html):
src/drum/engine/worklet-drum.js DSP core (AudioWorklet thread): 16 one-shot pad
voices, sample-accurate sequencer, choke groups
src/drum/engine/drumtables.ts the 4 drum wavetables (THUD, CRACK, TINE, GRIT)
src/drum/engine/drum-synth.ts AudioContext, drum worklet + master FX graph
src/drum/params.ts canonical per-pad + global parameter table
src/drum/seq.ts pattern data model + swing/step timing math
src/drum/kits.ts factory kits + localStorage user kits
src/drum/store.ts Zustand store: params, patterns, chain, transport
src/drum/components/ pads, panels, step sequencer, FX rack
src/drum/hooks/ computer-keyboard + MIDI pad input
The BL-1 bassline (TypeScript, fourth vite entry at bass/index.html):
src/bass/engine/worklet-bass.js DSP core (AudioWorklet thread): mono acid voice
(osc + sub + SVF + envs + accent/slide),
sample-accurate pitch sequencer, synced LFO
src/bass/engine/bass-synth.ts AudioContext, bass worklet + master FX graph
src/bass/params.ts canonical parameter table
src/bass/seq.ts pitch pattern data model (note/oct/acc/slide)
src/bass/patches.ts factory patches + localStorage user patches
src/bass/store.ts Zustand store: params, patterns, chain, transport
src/bass/components/ osc/filter/env panels, keys, pitch seq, FX rack
src/bass/hooks/ computer-keyboard + MIDI note input
The SQ-4 session launcher (TypeScript, fifth vite entry at seq/index.html):
src/seq/protocol.ts session document schema, clip payload layouts,
base64 codec, shared-timebase boundary math
src/seq/factory.ts the NEON TALE factory session: 6 scenes of
handcrafted clips in each machine's format
src/seq/devices.ts SeqDevice adapters wrapping the three engines
src/seq/rig.ts shared AudioContext, per-track gains/analysers,
final limiter
src/seq/store.ts the conductor: owner/queue state, quantized
scheduling, ack handling, mute/solo gains
src/seq/model.ts pure launcher rules + pattern-byte previews
src/seq/components/ top bar, track heads, scene rows, clip cells,
now-playing footer, SUM scope, VU meters