# MIDI playback — opportunity & design-space framing Framing for a **MIDI-triggered audio sampler** that plays back ReaSampler's captured banks. This is a **discussion-shaping doc, not a build plan** — no phase, no PLAN.md points, no settled forks yet. It exists so Daniel and the-boss can react to an honest map of the option space before anything is scoped. Status: framed by product-designer (2026-07-26), **revised 2026-07-26** to correct a material omission — the earlier draft missed REAPER's **VST-host bridge**: a VST/VST3 plugin *hosted inside REAPER* can call back into REAPER's own API from within the plugin, resolving API function pointers by name over the host `audioMaster` callback. That single fact changes the integration seam (§2), the "one product or two" question (D6), and the JSFX-vs-native calculus (D1/D2). The corrections are made plainly in-place, not appended as a footnote; where the prior draft was too pessimistic about what a native instrument can reach, it is fixed and the correction is flagged. Grounded in the vendored REAPER SDK headers (`vendor/reaper-sdk/sdk/reaper_plugin.h`, `reaper_plugin_functions.h`, `video_processor.h`, `reaper_plugin_fx_embed.h`) and REAPER's published VST-extensions SDK page (`reaper.fm/sdk/vst/vst_ext.php`) — the plugin-format claims below are checked against those, not asserted. The genuine forks are flagged as **Daniel's to decide**; nothing here pre-decides them. > **The bridge, stated once, up front (the correction).** A hosted VST is not limited to > scraping bank WAVs + JSON off disk. REAPER hands the plugin its host callback; calling > `hostcb(&effect, 0xdeadbeef, 0xdeadf00d, 0, "FunctionName", 0.0)` resolves *any* REAPER > API function by name — the same string-keyed API surface the extension uses (verified: > `video_processor.h` line 44 imports `video_CreateVideoProcessor` exactly this way; > `GetProjExtState`/`SetProjExtState`/`EnumProjExtState` are ordinary entries in that same > string-keyed table, `reaper_plugin_functions.h` lines 8376/8796/9969). The plugin can > also fetch its **host context** — the track/take/project it's instantiated in (sibling > opcode `0xdeadf00e`, `video_processor.h` line 40; CLAP has `clap_get_reaper_context`, > `reaper_plugin.h` line 142). **Consequence:** a native ReaSampler instrument can read > the *same* `"reasampler"` project ext-state that `persist` writes — live, project-aware, > following the active project — not a file it re-parses off disk. This is a > REAPER-VST-specific capability (it exists because the plugin is hosted *in REAPER*), and > it is the thumb on the scale the prior draft failed to weigh. The cost of leaning on it > is stated honestly in D1/D2: it makes the native plugin **REAPER-coupled.** --- ## The need, stated plainly ReaSampler today **captures and organizes** audio into per-project banks (on-disk 32-bit float WAVs + a JSON index, multi-bank via `bank_book`). It has **no way to play those samples back from a MIDI keyboard.** The panel can *audition* a sample on click — but a one-shot, one-voice audition is not an instrument. There is no key→sample mapping, no velocity response, no polyphony, no envelope, no round-robin. The ask: *what would let me play my captured banks MIDI-triggered, and what does the REAPER surface actually allow toward that?* The rest of this doc is organized as the task framed it: (1) the plugin-format reality, honestly; (2) the integration seam between capture and playback; (3) product shape and scope tiers; (4) the forks that are Daniel's to call. --- ## 1. The plugin-format reality (the honest part) **The load-bearing fact: a REAPER extension cannot be a MIDI-triggered instrument.** This is not a limitation we can engineer around by trying harder — it is what the plugin *format* is. ReaSampler today is a `reaper_*.dll` loaded at startup via the extension API (`ReaperPluginEntry`). That format gives an extension enormous reach over the *project* — tracks, items, actions, rendering, project state — but it is **not a node in any track's audio signal chain.** An instrument track's "read live MIDI, emit audio per-voice" contract belongs to VST/VST3/CLAP/JSFX plugins, which REAPER hosts through an entirely different mechanism. What the extension SDK *does* expose near audio/MIDI, verified in the headers, and why none of it is "be an instrument": - **`Audio_RegHardwareHook` + `audio_hook_register_t::OnAudioBuffer`** (`reaper_plugin.h` ~1228). This is a real-time callback into the audio thread, called pre- and post-REAPER-processing, handing you `GetBuffer(isOutput, idx)` for the **hardware I/O** channels. It is a *hardware tap*, not a track insert. You could technically mix sample playback into the hardware output buffer here — but you'd be bypassing REAPER's entire routing, metering, recording, and rendering path. Output from this hook **cannot be recorded, rendered, routed, or FX-processed** as track audio. It is the wrong tool for an instrument by construction, and it runs in the audio thread with all the real-time-safety hazards that implies. Rejected on sight for this purpose — noting it only to be complete. - **`kbd_OnMidiEvent` / `kbd_OnMidiList`** (`reaper_plugin_functions.h` ~4138). REAPER *informs* the extension of MIDI events (for action-triggering — this is how MIDI can fire the capture/insert actions we already plan to bind). It is **notification, not a routable MIDI input** feeding a synth voice allocator. Great for "MIDI note fires the capture-to-slot action"; useless as an instrument's note input. - **`PlayPreview` / `PlayTrackPreview` + `preview_register_t`** (`reaper_plugin.h` ~1302). This is REAPER's **preview-playback** mechanism, and **ReaSampler already uses it** — `bank_panel.cpp` streams a bank WAV through a caller-owned `preview_register_t` for click-audition (`PCM_Source_CreateFromFile` + `PlayPreview`). This is the one audio-emitting surface the extension already drives, and it is load-bearing for the hybrid option below. But note its shape: it plays *a* source at *a* position with *a* volume — it is a fire-and-forget preview, **not a polyphonic, per-note, velocity-scaled, envelope-shaped voice engine.** You do not get MIDI→voice allocation for free; you'd be building a sampler engine on top of a preview primitive. - **`pcmsrc` registration + `PCM_source` subclassing** (`reaper_plugin.h` ~804, the `PCM_SOURCE_EXT_*` surface). An extension *can* register a custom `PCM_source` type. This is the surface REAPER itself and extensions like SWS use for custom media. It is a real seam — but it produces a *media source* (something an item plays), not a *live-MIDI instrument*. Interesting for "a bank-backed media source" but not the keyboard-instrument ask. **Conclusion, stated for the record:** to get a genuine MIDI-triggered instrument — play a note, hear the mapped sample, velocity-scaled, polyphonic, with an envelope — the playback engine must be a **standard instrument plugin** (VST3 / CLAP / JSFX), *not* the extension. The extension SDK is the wrong format for that job, and this is the single most important thing for Daniel to internalize before scoping anything. ### The three honest options **Option A — a real VSTi/instrument plugin (JUCE or bare VST3/CLAP SDK) that reads ReaSampler's banks.** A separate build artifact: a VST3 (and/or CLAP) sampler plugin that the user instantiates on an instrument track. It maps samples across the keyboard and plays them MIDI-triggered with a real voice engine. This is the "sophisticated sampler" answer. - *Gives you:* everything an instrument is — polyphony, velocity, envelopes, the works, fully integrated into REAPER's routing/render/record path like any VSTi. **And, because it's hosted in REAPER, it reaches ReaSampler's project state directly via the VST-host bridge** — it doesn't merely parse bank JSON off disk, it can read the live `"reasampler"` ext-state `persist` writes, know its own host project, and follow the active project. The prior draft treated Option A as a divorced file-reader; that was the omission. A native ReaSampler instrument can be **natively, tightly integrated with the extension's project state**, not loosely coupled through a file format alone. - *Costs:* a **second codebase in a second plugin format**, almost certainly a new dependency (JUCE is the pragmatic choice; bare VST3 SDK is more code, CLAP is leaner but younger). It is a real DSP/voice-engine build, not a weekend. Cross-platform DSP, its own build/release/signing story, its own UI toolkit. This is a **product-sized commitment**, not a feature. And leaning on the bridge for state means the instrument is **REAPER-coupled** — it stops being a portable VST that runs in any host and becomes a REAPER companion. That may be exactly right (see D6), but it is a real narrowing and must be a conscious choice, not a side effect. **Option B — a JSFX sampler.** JSFX is REAPER's built-in scriptable plugin format (text `.jsfx` files, JIT-compiled by REAPER, hostable as an instrument). A JSFX instrument *can* receive live MIDI and emit audio on a track. It can load samples (`Xen`-style file reads / the JSFX file/serialize API) and play them back. - *Gives you:* a real in-track instrument with **zero new binary, zero new SDK, zero JUCE** — ships as a text file alongside the extension, cross-platform for free (REAPER runs the JIT everywhere it runs). - *Costs:* JSFX is a constrained DSP scripting language, not C++. A polyphonic multisample engine with round-robin/velocity-layers/streaming is *doable* but you're writing DSP in JSFX's idiom, and large-sample streaming / disk I/O is more awkward than in a native plugin. Reading ReaSampler's JSON index from JSFX is friction (JSFX is not a general-purpose file parser). Best fit for a **minimal-to-mid** sampler, a real ceiling for a **sophisticated** one. - *What JSFX can't match, now that the bridge is on the table:* the VST-host bridge is a **VST/VST3/CLAP capability** — it is how a *hosted plugin* resolves REAPER API pointers. JSFX is not a hosted VST; it has REAPER integration of its own kind (`ext_noinit`, `file_*` ops, the shared `gmem[]`, `slider`/parameter plumbing), but it does **not** get the same string-keyed REAPER-API surface a native VST does, and in particular has no clean, native path to read `persist`'s `"reasampler"` project ext-state the way a native VST can. On the JSFX path, the seam is genuinely "read a file ReaSampler wrote next to the bank" (hence the sidecar note in §2). This asymmetry is new information: the bridge makes **native meaningfully more integratable** than the prior draft assumed — it's not just "native is more powerful DSP," it's "native can be *part of ReaSampler's state model*, JSFX stays a file-coupled consumer." **Option C — the hybrid (recommended framing to explore first).** Keep the extension as the sole owner of **capture + organization** (which is its whole existing identity and the load-bearing "capture and placement are separate acts" principle). Add a **separate instrument** (Option A *or* B) that **consumes the banks** as a shared-artifact contract. The extension never becomes an instrument; the instrument never captures. Each does what its format is good at. This is the honest shape of the whole thing — the two options above are really "which instrument technology" *within* the hybrid, because the extension is staying regardless. The real fork, then, is **not** "extension vs. plugin" (the extension stays either way) — it is **"which instrument format consumes the banks: JSFX or native VSTi/CLAP,"** and **"how much sampler do we actually want."** Those are §3 and §4. --- ## 2. The integration seam The prior draft assumed one seam: **the bank folder (project-relative WAVs) + the bank/index JSON on disk**, with the playback engine as a file-reader. That's still the seam for *sample audio* (WAVs are files; there's no getting a live PCM stream across the bridge, nor would you want to). But for **everything that isn't the raw audio** — the index, the mapping data, which project's bank is active — the bridge opens a **second, richer seam** the prior draft missed: - **File seam (audio + a portable snapshot).** WAVs on disk; index/mapping as JSON or a sidecar. Tool-agnostic, host-agnostic, portable. Any instrument (JSFX or native, in REAPER or any other host) can consume it. This is the *only* seam JSFX gets, and the *fallback/export* seam for native. - **Live-state seam (native-in-REAPER only).** A native VST instance reads the `"reasampler"` project ext-state directly via the bridge (`GetProjExtState` / `EnumProjExtState`, resolved by name over `hostcb`), and knows its own host project via the context callback. It sees what `persist` last wrote, follows the active project, and needs no "point me at the right bank folder" wiring — it *asks REAPER* which project it's in. This is strictly more than the file seam and it's REAPER-coupled by construction. The design question below — "is the JSON sufficient, or does playback need mapping the index doesn't carry" — is unchanged. What the bridge changes is *where that mapping can live and how the instrument gets it*: for native, the mapping can be **live shared state** between extension and instrument, not just a file handed across. **What the current index carries** (from `bank_model`'s `Sample`, per CONTEXT.md §Data model): id, display name, relative path, source range, channel count, sample rate, length, capture tempo, an **optional key**, peak/RMS/LUFS, content hash, tier, provenance, timestamp. Notably it *already* has an optional key field and capture tempo — the seeds of pitch-mapping are there. **What a playback engine additionally needs** — none of which the index carries today: - **Root note** (the pitch the sample was recorded at, so it can be repitched across the keyboard). The optional `key` is close but is "musical key," not "root MIDI note" — different thing. - **Key range / zone** (low note, high note) — which keys trigger this sample. - **Velocity layers** (which sample plays at which velocity band; a mapping of samples to velocity zones). - **Round-robin groups** (cycle through N samples on repeated same-note hits). - **Loop points** (sustain loop start/end for held notes; sample-accurate, zero-crossing-aware — CONTEXT already flags loop/zero-crossing handling as day-one-relevant for wavetable material). - **Amplitude envelope** (ADSR) and optionally filter/pitch envelopes. - **Tuning/gain trim** per sample. **The design decision this forces:** does the *mapping* (key ranges, velocity layers, round-robin, envelopes) live — - **(i) in the bank index** (ReaSampler owns the instrument definition; the plugin is a dumb player of a ReaSampler-authored map), or - **(ii) in the instrument plugin's own state** (the plugin owns the map; the bank is just a WAV+metadata pool it maps *over*), or - **(iii) split** — the bank carries per-sample *intrinsics* (root note, loop points — facts about the file), and the instrument carries the *arrangement* (zones, layers, envelopes — a performance choice)? **(iii) is the principled answer and worth leading with.** It mirrors ReaSampler's own deepest instinct — the *capture/placement separation*. Root note and loop points are **facts about the captured file** (analogous to sample rate, length, peaks) and belong in the bank, computed or set at/after capture. Key zones, velocity layers, envelopes, round-robin are a **performance mapping** — a creative arrangement of those facts — and belong in the instrument. This keeps the bank a clean, portable, tool-agnostic library (a bank is still just WAVs + facts, readable by anything) while the instrument owns the opinionated part. It also means **the bank index grows only by file-intrinsic fields** (root note, loop points), which is a small, safe, additive change — exactly the kind of seam ReaSampler already knows how to add (mirror of how `provenance` was added as a field in M1 and populated later). **A note on format (revised for the bridge):** the friction is asymmetric, and more so than the prior draft said. - *Native (Option A):* doesn't just "read the bank JSON off disk" — it can read the map out of **live project ext-state** via the bridge (whichever of i/ii/iii you choose, the extension and instrument can share the *same* `"reasampler"` state rather than one writing a file the other re-parses). The `.wav` audio is still files; the mapping need not be. Cleanest possible integration. - *JSFX (Option B):* no bridge — reading arbitrary JSON is friction, so the seam likely needs a **simpler sidecar** (a flat `.txt`/key-value map ReaSampler writes next to the bank, JSFX-parseable). On the JSFX path the seam is unavoidably file-based. That's a concrete cost of JSFX and a reason the seam design and the format choice stay coupled. **Portability caveat (partly dissolved on the native path).** The bank is project-relative and travels with the `.rpp` (M4 machinery). Any *file-seam* consumer must resolve paths the same way — so a JSFX instrument still needs to be told the current project bank folder (saved in its own state), a real wiring detail, don't hand-wave it. But a **native** instance sidesteps this: via the context callback it asks REAPER which project it's in, then reads that project's bank location straight from ext-state — no "point me at the folder" step. What was a shared wiring cost is now a **cost only on the JSFX path.** Note the flip side: a native instrument that resolves its bank *only* through the bridge won't work outside REAPER at all — so if a portable/exportable instrument is ever a goal, keep the file seam as the source of truth and treat the live-state seam as an accelerator, not the sole path. --- ## 3. Product shape & scope tiers Sketched as tiers, minimal → sophisticated. The point of tiering is that **Tier 0 delivers the core promise** ("play my captured banks from a MIDI keyboard") and each tier above is optional depth, not a prerequisite for the one below. **Tier 0 — "the bank plays."** One sample mapped chromatically across the keyboard from a root note; monophonic-or-basic-polyphony; a simple amp envelope; velocity → volume. Point one bank sample at a MIDI track and play it repitched. *This is the smallest thing that delivers the promise* and is the honest MVP. On the JSFX path this is a genuinely small build; on the native path it's the skeleton of the plugin. **Tier 1 — "a keymap."** Multiple samples zoned across the keyboard (key ranges), each with its own root note. Now a captured *kit* (several one-shots) or a *multisampled instrument* (same instrument sampled at several pitches) plays correctly. This is where the **root-note + key-range** seam fields (§2) earn their place. Still one sample per key-region. **Tier 2 — "expressive."** Velocity layers (soft vs. hard hits pick different samples), round-robin (repeated hits cycle samples — the anti-machine-gun feature), full ADSR, per-sample tuning/gain trim, sustain loops for held notes. This is a *real* sampler and where "sophisticated" lives. It's also where the mapping data (§2) gets rich enough that the "who owns the map" fork (i/ii/iii) really bites. **Tier 3 — "instrument polish."** Filter + filter envelope, LFOs, per-voice pan, choke groups (hi-hat open/closed), maybe a modest built-in FX slot. Standard sampler feature creep. Explicitly *not* needed to deliver value; a direction to leave room for, not commit to. **The recommended reading:** Tier 0–1 is the "does this even belong in ReaSampler's world" proof. If the answer is yes, Tier 2 is where it becomes a tool people reach for. Tier 3 is optional forever. **Scope the decision at Tier 0–1 first** and treat 2/3 as held futures — don't let a Tier-3 feature list drive the format choice. ### The one-source-multiple-views angle Worth flagging because it recurs in Daniel's work: the bank is **one source** (WAVs + facts). The panel is one view (organize/audition). A MIDI instrument is **another view of the same source** (play). That's a clean framing — the instrument doesn't fork the bank, it consumes it — and it argues for keeping the bank as the single authoritative artifact and the instrument as a pure consumer (seam option ii/iii, never "the instrument keeps its own copy of the samples"). --- ## 4. Risks & open decisions — Daniel's to call None of these are pre-decided here. Each is a genuine fork. **D1 — Instrument format: JSFX vs. native VSTi (VST3/CLAP, likely via JUCE).** The central fork. JSFX = no new binary, no JUCE, cross-platform free, real ceiling on sophistication, awkward bank-JSON reading, **and no VST-host bridge** (file-seam only). Native = full sophistication, clean integration, **plus the bridge**: live `"reasampler"` ext-state, project-awareness, embedded TCP/MCP UI (see below) — at the cost of a whole second codebase in a second format with its own build/release/dependency/signing story, **and REAPER-coupling if it leans on the bridge.** *What the bridge does to the native column (the honest update).* The prior draft's native pitch was "more DSP power + parses JSON cleanly." The bridge adds a category the prior draft didn't weigh: native can be **part of ReaSampler's live state model**, not a file-coupled consumer. That is a real pull toward native beyond raw DSP — it's the difference between "an instrument that reads a ReaSampler export" and "an instrument that *is* ReaSampler's playback surface." It doesn't make JSFX wrong; it makes native's ceiling higher and its integration tighter than I credited. Weigh it against the coupling cost: a bridge-dependent VST is a **REAPER-only** artifact, no longer a portable VST. *My lean, updated but unchanged in direction, for discussion only:* if the goal is Tier 0–1, **still prototype in JSFX first** — it proves the *sampler value and the key/velocity seam* with near-zero format commitment, and the file seam you design there is exactly the portable/export seam a native build would keep anyway. The bridge is an *integration* advantage, not a *does-the-sampler-work* advantage, so it doesn't change what the cheapest proof is. Reach for native when either (a) Tier 2+ sophistication is a firm goal, or (b) **tight live integration with ReaSampler's project state is itself the point** — that second trigger is new, and it's the bridge's real contribution to this fork. **D2 — Whether to take a JUCE (or any external plugin-SDK) dependency at all.** The project today is a clean C++ extension with two vendored submodules and a proud pure-core/shell discipline. A native instrument means a *third* major dependency and a second build target of a fundamentally different kind. That's a real architectural weight. JSFX sidesteps it entirely. Flagging it as its own decision because "should we depend on JUCE" is a bigger standing commitment than "should we build a sampler." *The bridge's bearing on D2.* Weigh two coupling costs, not one. A native instrument that uses the bridge takes on **both** a plugin-SDK dependency (JUCE/VST3/CLAP) *and* a REAPER-API coupling (the same `reaper_plugin_functions.h` surface the extension binds — so the instrument would vendor and bind against it too, likely resolving pointers via `hostcb` rather than the extension's startup path). The upside is that the pure sampler core stays REAPER-free and testable regardless (D3); it's only the *shell* that touches the bridge, so the coupling is contained where the project already puts REAPER coupling. The downside is plain: choosing the bridge means choosing a REAPER-only instrument. If Daniel wants a sampler that also runs in other hosts, the bridge is off the table and D1's native pitch loses its integration edge — collapsing back toward "native = more DSP power only." **D3 — Does the pure-core discipline survive the format boundary?** ReaSampler's identity is *pure REAPER-free testable core + thin shells*. A sampler's **voice engine, envelope math, key/velocity mapping, and repitch logic are exactly the kind of pure, testable core** this project excels at — they could live in a REAPER-free, DAW-free, unit-tested module (mirror of `bank_model`/`peaks`/`view_mode_model`) with the plugin format (JSFX or VST3 wrapper) as the thin shell around it. **This is the most ReaSampler-native way to build it** and I'd argue strongly for it regardless of D1: the sampler DSP core is pure and tested; the format is a shell. The open question is whether that discipline can hold across a *different plugin format* — with native it's clean C++ so yes; with JSFX the "pure core" would be JSFX code, harder to unit-test in the CTest harness. That tension is real and feeds back into D1. **D4 — Where the mapping lives (seam fork i/ii/iii from §2).** Bank-owned map, instrument-owned map, or split (file-intrinsics in the bank, performance-map in the instrument). *My lean:* **(iii) split** — it's the one that honors ReaSampler's capture/placement instinct and keeps the bank tool-agnostic. But it's Daniel's call whether ReaSampler should author instrument definitions at all, or stay purely a sample library that a *separate* mapping tool arranges. **D5 — Cross-platform.** The extension is already cross-platform (SWELL). JSFX inherits that for free. A native plugin re-opens the full cross-platform DSP + UI + build matrix (Win/mac/Linux, code-signing on mac, etc.) as a *separate* artifact. A cost that lands entirely on the native path. **D6 — Is this even one product?** The reframe worth surfacing: ReaSampler's thesis is "a precision *capture/organize* tool; capture and placement are separate acts." A MIDI-playback instrument is a *different act* — playback. There's a legitimate reading where the instrument is a **companion product** that shares the bank format, not a feature *of* ReaSampler — the way a sample library and a sampler that reads it are related-but-distinct products. That framing might keep ReaSampler sharp (it stays the capture tool it is) while letting the instrument evolve on its own clock and format. The alternative reading — it's all one integrated sampler-workstation — is also coherent. *Re-argued with the bridge on the scale (the prior draft weighed this fork without it).* The prior draft implicitly assumed the two products could only ever share a **file format** — which makes "companion product" feel clean and natural, because a shared file is exactly what independent products share. The bridge changes the terms. A native instrument can be **tightly, live-integrated with ReaSampler's project state**: same `"reasampler"` ext-state, same project identity, following the active project — a degree of coupling that is *more than* "two products that happen to read the same files" and starts to look like "two faces of one tool sharing one state model." That is a genuine thumb toward the **one-integrated-product** reading. It's not decisive, and I won't pretend it is: - It only applies if the instrument is **native** (JSFX can't reach the bridge, so a JSFX instrument *is* naturally a file-coupled companion — the format choice and this fork are entangled: pick JSFX and you've half-answered D6 toward "companion"). - It's an *available* integration, not a *required* one — a native instrument could still be built to the file seam only, deliberately staying a loose companion for portability. The bridge widens the option, it doesn't force it. - The tight-integration reading buys **REAPER-coupling**; the companion reading buys **portability and independent evolution.** Those are real, opposed goods, and the bridge doesn't resolve which Daniel wants — it just makes the integrated option *materially more capable* than the prior draft credited, so the companion reading no longer wins by default on "well, they can only share a file anyway." **Net:** the bridge strengthens "one integrated product" from a coherent-but-equal option into the one with the stronger technical affordance — *if* native-in-REAPER is the path. It does not pre-decide the fork. This is still the highest-order question and it's Daniel's to answer, but he should answer it knowing the integrated option is richer than the file-sharing picture implied — and knowing that choosing JSFX quietly tilts it the other way. **D7 — Embedded TCP/MCP UI (new option the bridge surfaces; native/CLAP only).** While verifying the bridge I found a second REAPER-VST-specific affordance the prior draft never mentioned: a hosted plugin can **draw its own embedded UI directly in the track/mixer control panel** (`reaper_plugin_fx_embed.h`: VST2 answers `canDo("hasCockosEmbeddedUI")` with `0xbeef0000` and draws via `effVendorSpecific/effEditDraw`; VST3 implements `IReaperUIEmbedInterface`; CLAP exposes `cockos.reaper_embedui`). Concretely: a native ReaSampler instrument could render a compact keymap/level strip *inline in the TCP/MCP*, not only in its own plugin window — the same surface REAPER's own JS/embedded FX use. This is **native/CLAP-only** (JSFX can't do it) and is pure polish, not a Tier-0 need — but it's a real integration affordance that only exists on the native-in-REAPER path, and it compounds the D1/D6 tilt: if "ReaSampler's playback surface, deeply woven into REAPER's UI" is the vision, this is a lever only native reaches. Flagging it for completeness, not recommending it — it's a Tier-3-ish nicety, deferred by default. --- ## What this doc is asking for A direction on the two highest-order forks, in order: 1. **D6 — one product or two?** Is the instrument a feature of ReaSampler, or a companion product sharing the bank format? Everything else sits under this. 2. **D1 — JSFX-first prototype, or straight to native?** Given a target of Tier 0–1 to start, and the pure-core-as-shell discipline (D3) held either way. Once those two are called, the seam fields (§2, D4) and the tier scope (§3) become concrete enough to write an actual phase spec. Until then this stays a framing doc with no PLAN.md footprint — deliberately, so we don't scope an instrument before deciding whether we're building one.