midiviz

Realistic violin sound in the browser

Research brief, 17 Oct 2026. Question: how do we turn the #033 fingering/bowing output into violin audio that sounds like a player, in real time or close to it, inside a web page?

Today #033 plays through soundfont-engine (oxisynth) with the 8 MB Creative GM soundfont: one looped sample per note, no bowing, no vibrato, no shifts. Everything below is measured against that.

1. Where violin realism comes from

A violin note is not a fixed waveform. Listeners hear:

The useful consequence: most of the realism is in the control signals (performance), and #033 already knows the string, position, shifts, bow direction, slurs and string crossings for every note. Any synthesis method we pick should be driven by those, not by bare MIDI notes.

2. Options

2.1 Better samples (baseline, hours of work)

2.2 Physical modelling (real time, small, fully controllable)

2.3 DDSP: neural control of a harmonic synth (real time, small)

2.4 RAVE / BRAVE: neural timbre transfer (real time, audio in → audio out)

2.5 MIDI-to-audio diffusion (highest fidelity, offline or render-ahead)

2.6 Suno-style text-to-music and open alternatives

Model Open Size / speed Follows a given score?
Suno, Udio No, cloud API only n/a No: text and lyrics prompts
MusicGen small (ONNX for transformers.js) Yes runs in the browser on WebGPU, slower than real time Melody variant follows a hummed contour loosely
Stable Audio Open Small (Stability/Arm) Yes, 341 M 11 s of audio in under 8 s on a phone No
Stable Audio 3 (paper) Small and medium open seconds on an M4 MacBook No (text, audio-to-audio)
ACE-Step (repo) Apache-2.0, 3.5 B; v1.5 under 4 GB VRAM fast on a GPU No
Magenta RealTime (blog) Apache-2.0, 800 M live, needs TPU/GPU, ~3 s control latency No (text/audio style)
Magenta RealTime 2 (blog, Jun 2026) Open weights, 2.4 B / 230 M 40 ms frames, ~200 ms control latency, Apple Silicon via MLX Takes MIDI as live conditioning

These models generate whole textures in a style. Apart from MRT2 none of them can play our violin line note for note, and none gives us bow, string or fingering control. MRT2 is the one to watch: a live, MIDI-steered model is close to “a neural instrument”, but today it runs only on Apple Silicon through a C++/MLX engine, not in a browser, and how exactly its audio follows the MIDI notes (pitch accuracy, articulation) is something we would have to test. Realistic uses for us: accompaniment or hall ambience behind the violin, not the violin itself.

3. Comparison

Approach Latency Download Control from #033 Realism (expected) Effort
GM soundfont (today) ~3 ms 8 MB notes only low done
CC0 SFZ samples ~3 ms 30–200 MB notes, velocity medium, static phrasing low
Waveguide + body + performance curves ~3 ms < 1 MB bow, string, shifts, vibrato medium-high, depends on tuning medium
DDSP violin (+ expression model) 5–20 ms (worklet) / phrase (tfjs) 4 MB f0, loudness high on sustained lines medium
Waveguide → BRAVE ~10–20 ms tens of MB full potentially high high, unverified
VIOLET / ViolinDiff seconds per phrase 100+ MB notes, techniques, dynamics highest published high, port needed
Suno-style / MRT2 200 ms – seconds GB none / loose MIDI high texture, wrong notes not for the solo line

4. Recommendation

Build one performance renderer and plug several synthesis back ends into it, then compare by ear.

  1. Performance renderer (strings/performance.js): turns the #033 result into control curves at ~250 Hz per voice: f0 (with vibrato rate/depth/onset, shift portamento whose length depends on shift distance, no vibrato on open strings), bow velocity and force (from note length, dynamics, bow direction, slurs, bow changes, string crossings), bow position, and string index. Rules come from 01_violinist_rules.md; later we can fit vibrato and portamento statistics to the TNUA or ViolinDiff recordings.
  2. Back end A, physical model: Faust bowed-string waveguide per string in an AudioWorklet, a modal or convolution body per instrument, then the existing hall reverb. Cheapest, lowest latency, works with the live MIDI keyboard, and the only option that uses the string and bow data directly.
  3. Back end B, DDSP violin: start with the magenta.js ddsp_violin checkpoint rendering phrases ahead of the playhead (fast to try, settles whether the timbre is worth it). If it is, move the decoder into the AudioWorklet (ONNX or hand-written GRU) for real time, and consider training viola and cello models with the DDSP-VST notebook.
  4. Reference: a CC0 SFZ violin as the “good samples” baseline, plus an offline VIOLET render of the same excerpt as the quality ceiling.
  5. Not now: Suno-style models for the solo line. Revisit Magenta RealTime 2 if a web runtime appears.

Open questions to answer while building: licence and availability of measured violin body impulse responses; whether a browser DDSP decoder holds up at 128-sample quanta on mid-range laptops; how much of the remaining gap to SWAM is control curves versus the string model itself.