Gridleak - Every Preset Starts With a Circuit
I've been playing through modelers and via MIDI for years - going all the way back to 2002 when I moved from a Mesa Dual Rectifier to a Marshall JMP1 pre-amp paired with random software of the time on my Powerbook G3, eventually evolving to Line6 and Fractal devices - and the thing that always nagged at me was the starting point for most presets.
Somebody dials in something that sounds good in their room, shares it, and the whole community builds on that foundation. Nothing wrong with it. But I kept wondering what it would look like to start from the actual circuit instead, and if it was even possible.
What if the bass knob position came from the capacitor values in the original tone stack? What if gain structure reflected the actual gain staging of the circuit? Not ear-tuned approximation - something derived from the physics of the amp itself.
That insane question became Gridleak.app.
The Corpus Problem
There are thousands of amp schematics floating around the internet. Sites that have been quietly hosting PDFs of vintage amp schematics for decades. Fender Blackface circuits. Marshall Plexis. Mesa Boogie Mark series. Vox AC30s. Dumble clones. Obscure Italian amps nobody's heard of.
The problem is they're PDFs. Scanned images of hand-drawn or CAD schematics, often from 50-year-old service manuals. You can't query them. You can't extract the component values. They just sit there, human-readable but machine-opaque.
Step one was acquiring them. I built scrapers and ended up with 44,663 amp schematics. Every manufacturer you've heard of and several hundred you haven't.
# The manifest builder — rebuilds from disk on every run
# so partial runs and manual additions stay accurate
def build_manifest(schematics_dir: Path) -> dict:
manifest = {}
for pdf in schematics_dir.rglob("*.pdf"):
source_mfr = f"{pdf.parts[-3]}/{pdf.parts[-2]}"
model = pdf.stem.replace("_", " ")
manifest.setdefault(source_mfr, {}).setdefault(model, []).append(str(pdf))
return manifestA manifest JSON. 11,440 unique entries. Now what?
Teaching a Model to Read Schematics
The next problem was getting useful data out of those PDFs.
A guitar amp schematic is a specific kind of document. The parts that matter for tone are the passive components in the tone stack - a classic passive EQ network sitting between the preamp and power amp stages. For FMV-topology amps (named after Fender-Marshall-Vox, the three families that defined the form), you're looking for specific resistor and capacitor values: the bass, mid, and treble pot resistors, the tone stack capacitors, the coupling caps. These values directly determine the frequency response curves of the amp.
So I used computer vision (my day job for the most part) to extract them. Each schematic gets a topology-specific prompt. BLACKFACE amps get one prompt. PLEXI amps get another. RECTO amps get another. Thirteen families, each with its own expected component ranges and schematic conventions.
The extraction pipeline runs two passes. First pass: fast and cheap, extracts component values and validates them against physical range gates (a 500kΩ NFB resistor is physically implausible; a 5Ω one is a bug). Second pass: anything that failed range validation goes back with more careful prompting and analysis.
Result: 5,040 extraction entries in the first 30 days. Component values for every amp in the corpus that had a readable schematic.
The Circuit Math Nobody Wanted to Do
Here's where it gets genuinely interesting.
The FMV tone stack has a well-understood transfer function. Given the component values, you can compute the actual frequency response curves - what happens to the bass frequencies when the Bass knob is at 5, at 10, at 0. Same for Mid, Treble. This isn't approximation. It's the actual physics of the circuit.
def compute_fmv_curve(components: ToneStackComponents,
bass: float, mid: float, treble: float) -> np.ndarray:
"""
Transfer function for FMV passive tone stack.
Returns frequency response in dB across 20Hz–20kHz.
"""
Rb = components.R_bass * bass
Rm = components.R_mid * mid
Rt = components.R_treble * treble
freqs = np.logspace(np.log10(20), np.log10(20000), 512)
H = _fmv_transfer(freqs, Rb, Rm, Rt,
components.C_bass, components.C_mid, components.C_treble)
return 20 * np.log10(np.abs(H) + 1e-10)I computed these curves for all 724 known-topology amps. A 1959 Marshall Plexi has a specific mid scoop that's different from a JCM800, which is different from a Soldano SLO. Not vibes - math.
The NFB (negative feedback) loop gets the same treatment. The feedback resistor ratio directly affects feel: compression on transients, perceived tightness, how the amp responds to pick attack. Sag comes from the power supply's output impedance under load. These are all computable from the schematic if you know where to look.
Behavioral Calibration from Neural Amp Models
Physics gets you most of the way there. But circuit math doesn't fully capture what a specific amp sounds like driven hard - what its harmonic saturation profile looks like, how the compression interacts with the output transformer.
For that, I built a matching pipeline against a large corpus of Neural Amp Modeler captures. NAM captures are behavioral fingerprints of real amps - trained on log-swept sine waves, they reproduce a specific physical amp's response with high fidelity.
The matching pipeline does fuzzy string matching between canonical amp IDs and capture names, with careful filtering: captures recorded as blends (mixing two amps) get excluded, ambiguous matches get flagged, only high-confidence single-amp matches get used.
For matched amps, the capture gives behavioral data - gain character, harmonic structure, drive headroom - that supplements the circuit math. The synthesis step knows whether this amp was modeled with warm clean headroom (Fender Deluxe) or compressed high-gain saturation (5150) because there's real behavioral data behind it.
Synthesizing 41,000+ Presets
The synthesis step takes all of this - FMV frequency curves, NFB parameters, NAM behavioral data where available - and produces specific values for each modeler.
Fractal Axe-Fx uses different parameter names and ranges than a Line6 Helix. The Amp block on an FM9 has different tone stack modeling behavior than the same block on an FM3. Each device gets a synthesis pass that accounts for its specific parameter space.
For the 74 amps with both circuit math AND NAM behavioral data: highly calibrated presets. For the 6,330+ amps that are UNKNOWN topology: characteristic-based profiles derived from whatever the circuit notes say - gain stages, NFB presence/absence, speaker type, era. Not as precise, but grounded in the actual circuit documentation.
The corpus regen script processes all of this:
Total work items: 30,185
Complete: 30,185
Failed: 0
Devices: axefx2, axefx3, fm3, fm9, helixFive devices. 6,037 unique amp channels. Zero failures on the final run.
The MIDI Problem
Getting a preset into a physical device is harder than it sounds.
Modern modelers from Fractal accept SysEx - System Exclusive MIDI messages - for bulk preset transfers. The format is device-specific and mostly undocumented. I reverse-engineered the Fractal Axe-Fx format by comparing known-good presets and working backward through the byte structure.
But the transport layer has two non-obvious requirements that took an embarrassing amount of debugging to figure out:
One: You have to open the device's MIDI Input before sending. Attach a midimessage event listener to every MIDIInput in the MIDIAccess object. Without this, the Axe-Fx device thinks no host is listening and silently drops the bulk transfer. No error. No acknowledgment. Just silence. The unit requires bidirectional MIDI presence even for what looks like a one-way transfer.
Two: All SysEx messages have to be burst-sent in a single JavaScript tick, no await between calls. Even await setTimeout(0) is enough latency that the unit's session state machine times out mid-stream and the preset never commits. The footer hash never writes. You get corruption or nothing.
// CORRECT: burst send, all messages in one tick
for (const msg of sysexMessages) {
output.send(msg); // no await here
}
// WRONG: looks reasonable, destroys everything
for (const msg of sysexMessages) {
await new Promise(r => setTimeout(r, 0)); // kills it
output.send(msg);
}The Web MIDI API, running in Chrome, delivering real SysEx to real hardware over USB. When it works, you tap a button on a webpage and a 1959 Marshall Plexi preset - derived from the actual schematic values - appears on your unit.
The Frontend
The product is a React app on Cloudflare Pages. With the kind of UI that respects that guitar players are often also engineers and don't need things dumbed down.
BrowsePage is where you find amps. 5,040+ in the library today (and more coming every week as I source them), filterable by manufacturer, tone category (British, American, Clean, Crunch, High Gain, Vintage, Modern), and quality tier. Skeleton loading states instead of spinners - content-shaped placeholders that hold layout space while data loads.
The PresetDetailPage goes deep. Every amp gets:
Confidence tier badge - Verified (full readable schematic), Close (some topology and values inferred due to degradation), or Pipeline (schematic sourced, parameters still improving to read it)
About section - tubes used, era, notable users, iconic recordings. The 1959 SLP entry lists Jimmy Page, Slash, Angus Young. Because it should.
Heard On - recordings that famously used this amp
Channel navigation - multi-channel amps show each channel separately
Direct install - one click to Web MIDI delivery to your connected device
The AI Generator
The browse library solves one problem: you know what amp you want. The generator solves a different one: you know what you want to sound like and you don't care what circuit gets you there.
The input is plain English. "Early 70s hard rock, bridge humbucker, cranked Marshall feel, some cab compression." "Clean Fender headroom that just barely breaks up on hard strums, lot of pick attack." "Modern high gain, tight low end, scooped mids, the kind of tone that sounds good in a dense mix." Whatever you'd say to a sound engineer in a studio.
What happens next is the interesting part.
The generator doesn't just throw your description at a language model and ask it to invent numbers. It works inside the parameter space the pipeline built. Five devices, each with their own amp block parameters, each calibrated against the corpus of circuit-derived values. The model knows that a "cranked Marshall feel" implies a specific gain staging range, a mid-forward EQ curve, a particular NFB feedback character - because those are the actual circuit properties of cranked Marshalls, extracted and synthesized across dozens of real schematics.
The result is a full signal chain. Not just the amp block - cab simulation, drive, modulation, delay, reverb. The whole chain gets synthesized together because that's how real rigs work. The amp doesn't exist in isolation from the cabinet. The cab choice affects which amp parameters make sense.
Input: "warm blackface clean, chimey top end, light reverb, slight breakup at the edge"
Output (Axe-Fx III, Amp block excerpt):
Amp Model: Deluxe Verb Nrm
Gain: 4.2
Bass: 6.8
Mid: 5.1
Treble: 7.4
Master: 6.0
...
Cabinet: 1x12 Blackface
Reverb: Spring, Mix 22%, Decay 1.8sThe Gain isn't 4.2 because it sounded okay in a bedroom. It's in that range because that's where blackface circuits live when you're asking for "slight breakup at the edge" - grounded in the actual gain staging of those amps, informed by dozens of extracted schematics from that family.
For people who know their gear, there's also a device-specific path. Select your hardware first - Axe-Fx II, Axe-Fx III, FM3, FM9, or Helix - and the generator synthesizes parameters specifically for that device's block architecture. The Helix's amp modeling works differently from the Fractal stack. The output reflects that. You're not getting a generic preset translated poorly into your device's format. You're getting one written for it.
And then, same as the browse flow: one click, Web MIDI, preset lives on your hardware.
The generator is the part I find hardest to explain to non-players and most immediately obvious to anyone who's spent time dialing in a modeler. The gap it closes isn't technical. It's the gap between knowing exactly what you want and knowing which 47 parameters to touch to get there.
The Architecture
FastAPI backend on Railway. Supabase for the preset database - 41,000+ rows, each with manufacturer, model, device, confidence score, R2 object key, download count. Cloudflare R2 for the actual .syx files. Stripe for billing (free tier with download limits, Pro for unlimited).
The interesting architectural piece is the Cloudflare Pages Functions layer. For SEO, I needed server-side rendering of the amp pages - Googlebot doesn't wait around for React to hydrate. CF Pages Functions runs at the edge and serves pre-rendered HTML for bots while SPA-routing normally for users. No Node.js. No Vercel. Runs in a V8 isolate at Cloudflare's edge nodes.
A/B testing for the landing page works the same way - a single middleware function intercepts requests to /, assigns a variant cookie, and PostHog tracks the assignment event on first mount. No third-party A/B tool. Zero cost.
What It Actually Is
Tens of thousands of amp schematics. Component value extraction via vision AI. Deterministic circuit math for frequency response. Neural amp model behavioral matching. Per-device parameter synthesis. Web MIDI SysEx transport to real hardware.
It's a long path between "I want a good Plexi preset" and getting one. But at the end of it, the preset is grounded in something real - the same physics that made the original amp sound the way it did out of the box.
There are 5,040 amps in the library today. Most of them you've never heard of. Some of them haven't been in production for 60 years. They all have presets now.