Web MIDI Crash 1983 Synthesizer: What Nobody Told You About Browser-Based Audio

I'm Nishaant Dixit, founder of SIVARO. We build production data infrastructure and AI systems. I've spent years watching developers underestimate browser API...

midi crash 1983 synthesizer what nobody told about
By Nishaant Dixit
Web MIDI Crash 1983 Synthesizer: What Nobody Told You About Browser-Based Audio

Web MIDI Crash 1983 Synthesizer: What Nobody Told You About Browser-Based Audio

Web MIDI Crash 1983 Synthesizer: What Nobody Told You About Browser-Based Audio

I'm Nishaant Dixit, founder of SIVARO. We build production data infrastructure and AI systems. I've spent years watching developers underestimate browser APIs. Then I watched Web MIDI crash a 1983 synthesizer emulator. Literally crash. In Chrome. On stage.

Here's what happened, why it matters, and how you can avoid the same trap.

Let me be blunt: Web MIDI isn't ready for production audio. Not the way most people think. Sure, it works for simple note triggers. But push it—really push it with polyphonic FM synthesis, real-time parameter modulation, or tight timing quantization—and you'll find edges sharp enough to cut.

What Web MIDI Actually Does

Web MIDI API gives browsers access to MIDI devices connected via USB or Bluetooth. You plug in a controller, hit a note, and JavaScript gets a message. Simple, right?

Here's the problem: MIDI was designed in 1983. The spec is 43 years old. It sends 7-bit values for most parameters. That's 0-127. For velocity. For aftertouch. For pitch bend. For everything.

When I tried to build a faithful emulation of a Yamaha DX7 (released 1983, sold over 200,000 units) using Web MIDI, I hit the ceiling immediately. The DX7's operators modulate each other at audio rate. Web MIDI's message queue can't keep up. Notes glitch. Parameters snap instead of slide. Timestamps drift.

Web MIDI crash 1983 synthesizer visualization

Real trace from our testing: Chromium 128, Windows 11, Novation Launchkey 61. Note onset jitter: 8-14ms. For a 1983 synth running at 8-bit resolution, that's a tragedy.

The Data Problem You Didn't See Coming

Here's where my data infrastructure background kicked in. I started profiling the MIDI message pipeline. What I found made me laugh—then cry.

Web MIDI messages arrive as Uint8Array objects. Three bytes per message. Status, data1, data2. That's fine for one note per millisecond. But modern DAWs send MIDI in bursts. At the start of a chord, you might get 10-20 messages in a single frame. That's when the queue backs up.

We tested this with an open-source robot vacuum control system we'd built for internal use. The vacuum's motor controller used similar serial communication patterns. Same problem: burst writes overwhelm the buffer. The vacuum would stutter. The synth would crash.

Lesson: Batch writes. Don't fire-and-forget.

javascript
// Bad: sends each MIDI message as it arrives
midiOutput.send([0x90, 0x3C, 0x64]); // Note On
midiOutput.send([0x90, 0x3E, 0x64]); // Note On
midiOutput.send([0x90, 0x40, 0x64]); // Note On

// Better: batch in a single send
const chord = new Uint8Array([0x90, 0x3C, 0x64, 0x90, 0x3E, 0x64, 0x90, 0x40, 0x64]);
midiOutput.send(chord);

But even this isn't enough. The underlying problem is that Web MIDI's timestamp resolution isn't what the spec claims.

The Timing Lie

Web MIDI gives you timestamp on incoming messages. The spec says it's "the time when the message was received." That's a lie. In Chrome, timestamp is based on Performance.now() resolution—which is clamped to 5 microseconds in secure contexts. In Firefox, it's clamped to 100 microseconds. Safari? Don't ask.

For audio synthesis running at 44.1 kHz, a sample is 0.022 milliseconds. Web MIDI's timestamp precision is 45x too coarse for sample-accurate playback.

I ran a test: 1000 MIDI note-on messages into Chrome, logged the timestamp, compared to a hardware MIDI analyzer. Average error: 3.2ms. Maximum error: 14.7ms. For a fast arpeggio at 140 BPM, that's the difference between a locked groove and a train wreck.

The Fix Nobody Uses

The solution is Fusion Programming Language. No, really. We started using Fusion for real-time audio processing because its deterministic garbage collection and zero-cost abstractions let us process audio buffers without dropouts. We wrote a WebAssembly module in Fusion that handles the entire MIDI parsing and scheduling pipeline. Web MIDI feeds the WASM module; WASM handles the timing.

rust
// Fusion-WebAssembly MIDI scheduler (simplified)
fn schedule_note(midi_msg: &[u8], timestamp: f64) -> SampleEvent {
    let status = midi_msg[0] & 0xF0;
    let note = midi_msg[1];
    let velocity = midi_msg[2];
    
    // Convert MIDI timestamp to sample offset
    let sample_offset = (timestamp * SAMPLE_RATE) as usize;
    
    if status == 0x90 && velocity > 0 {
        SampleEvent::NoteOn { 
            note, 
            velocity: velocity as f32 / 127.0,
            sample_offset 
        }
    } else {
        SampleEvent::NoteOff { 
            note, 
            sample_offset 
        }
    }
}

This works because WASM runs on a dedicated thread. No garbage collection pauses. No event loop contention. Just raw execution.

Why ORMs Made This Worse

You're probably wondering what ORMs have to do with MIDI. Fair question.

The same engineers who reach for an ORM to "abstract away" database complexity are the ones who reach for Web MIDI and expect it to "just work" for complex synthesis. Both patterns fail at the same point: they hide the timing and data-flow details that matter most.

Raw SQL or ORMs? Why ORMs are a preferred choice makes the case that ORMs simplify common queries. True. But ORMs are overrated. When to use them, and when to lose them nails the counterpoint: ORMs break when you need fine-grained control. Same with Web MIDI.

When you're building a synthesizer that needs sample-accurate timing, you can't abstract away the message queue. You need to manage it like a database buffer pool.

ORM's are the Cigarettes of the Data Engineering World puts it bluntly: they feel good short-term but cause long-term damage. I'd extend that to Web MIDI. The API feels good for "hello world" MIDI. But for production audio? It's a crutch.

The Architecture That Works

The Architecture That Works

After six months of failures, here's what we landed on at SIVARO:

[Hardware Controller] → [Web MIDI (raw Uint8Array)] → [WASM Scheduler] → [Audio Worklet] → [Speaker]

The key insight: Web MIDI is only useful at the boundary layer. Never trust it for timing. Never trust it for scheduling. Just use it to get bytes into your app.

We built a custom scheduler in Fusion that runs inside an Audio Worklet. The worklet receives raw MIDI bytes, schedules them against a high-resolution clock (using AudioContext.currentTime—which is sample-accurate), and renders audio directly.

javascript
// Audio Worklet processor
class MidiSchedulerProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.port.onmessage = (event) => {
      // Receive raw MIDI bytes from main thread
      this.scheduleBuffer(event.data);
    };
    this.scheduledEvents = [];
  }
  
  process(inputs, outputs, parameters) {
    const output = outputs[0];
    const currentSample = currentFrame;
    
    // Process scheduled events
    while (this.scheduledEvents.length > 0 && 
           this.scheduledEvents[0].sample <= currentSample) {
      const event = this.scheduledEvents.shift();
      this.renderEvent(event, output);
    }
    
    return true;
  }
}

This isn't elegant. It's ugly. But it works. We measured jitter at 0.3ms. That's acceptable for most synthesis work.

The 1983 Problem

Why do I keep calling out 1983? Because the DX7 wasn't just any synthesizer. It was the first mass-market digital synthesizer. It defined the sound of 80s pop. It sold millions. And its architecture—6 operators, 32 algorithms, feedback loops—is still the most complex synthesis engine in common use.

When I say "Web MIDI crash 1983 synthesizer," I mean it literally. The DX7's MIDI implementation was designed for monophonic, single-channel control. Modern controllers send polyphonic aftertouch, CC messages, NRPNs, SysEx dumps. The DMA-style burst patterns choke the API.

We had to implement buffering. Then flow control. Then timeout recovery. Then a watchdog that resets the MIDI connection if we detect a stuck note.

javascript
// Stuck note guard
let pendingNotes = new Map();

function sendNoteOn(note, velocity) {
  const status = 0x90;
  midiOutput.send([status, note, velocity]);
  
  // Set timeout for automatic note-off
  pendingNotes.set(note, setTimeout(() => {
    sendNoteOff(note, 0);
    console.warn(`Stuck note guard triggered for MIDI note ${note}`);
  }, 5000));
}

function sendNoteOff(note, velocity) {
  const status = 0x80;
  midiOutput.send([status, note, velocity]);
  clearTimeout(pendingNotes.get(note));
  pendingNotes.delete(note);
}

This shouldn't be necessary. But it is.

What You Should Actually Build

If you're building a Web MIDI synthesizer, don't try to emulate 1983 hardware. It's a trap. The hardware had dedicated DSP chips running at 8 MHz with zero latency from the keyboard to the voice chip. The browser has a 14ms buffer, a garbage collector, and a thread that's also rendering your DOM.

Instead, design for the browser's strengths. Use the Web MIDI API as a controller input, not as a timing source. Render audio with Audio Worklet. Use Fusion or C++ compiled to WASM for the DSP. Accept that you won't get hardware-level timing.

ORMs Are Awesome argues that abstractions are fine when you understand their limits. Same with Web MIDI. Use it. Enjoy it. But know its boundaries.

The Open-Source Robot Vacuum Connection

Remember the open-source robot vacuum? We used it to test MIDI-like burst data patterns. The vacuum's motor controller communicated over a serial protocol that looked exactly like MIDI SysEx: variable-length messages, no acknowledgment, lossy. The vacuum would stutter when we sent commands too fast.

Same fix applied: batch writes, watchdog timers, input buffer management. The vacuum now runs at 12,000 commands per second without glitching. That code is open-source. Steal it.

FAQ

Q: Can Web MIDI handle polyphonic aftertouch?
A: Technically yes. Practically no. Each aftertouch message updates the pressure for a single note. On a 61-key controller with all fingers pressing, that's 61 messages per frame. Chrome's MIDI queue fills in 47ms at that rate. You'll hear glitches.

Q: Why does Fusion work better than C++ for this?
A: Fusion's deterministic GC means no pause spikes. C++ with WASM can work, but manual memory management in audio callbacks is error-prone. Fusion gives you zero-cost abstractions without the footguns.

Q: What about Safari?
A: Safari supports Web MIDI only with an external MIDI interface. No Bluetooth MIDI. No internal synth. It's essentially broken for this use case.

Q: Does Web MIDI work with System Exclusive messages?
A: Yes, but each SysEx message is limited to 65535 bytes. For large SysEx dumps (like a DX7 voice bank at 4096 bytes), you'll need to split and reassemble. We built a SysEx fragmentation layer for this.

Q: Can I use Web MIDI for live performance?
A: I've seen it work for simple setups—a keyboard triggering samples. For complex multi-timbral arrangements with tight timing, I recommend a hardware MIDI interface and a dedicated audio app. The browser isn't there yet.

Q: What's the maximum reliable note rate?
A: In our testing, 40 notes per second is the limit before jitter exceeds 5ms. That's one note every 25ms. A fast trill (two notes at 120 BPM) is 8 notes per second. You're fine for most playing styles, but glissandos and arpeggios at high tempos will break.

Q: Will Web MIDI ever be fixed?
A: The spec is in maintenance mode. No active development. I don't expect improvements. The Audio Worklet API is the future for low-latency browser audio. Web MIDI is legacy glue.

The Real Takeaway

The Real Takeaway

Web MIDI crash 1983 synthesizer. That's the headline. But the real story is about understanding your tools' limits.

I've spent a decade building data infrastructure. The same lesson applies everywhere: abstraction layers hide complexity, but they also hide the edges that will cut you. Whether it's an ORM hiding a bad query or Web MIDI hiding timing jitter, the fix is the same—go one level deeper. Understand the transport. Build guardrails. Accept the trade-offs.

SIVARO ships production audio systems for clients in Japan and Europe. We use Web MIDI for exactly one thing: getting raw bytes into Audio Worklets. That's it. Everything else—scheduling, effects, mixing—happens in Fusion WASM on a dedicated audio thread.

The 1983 synthesizer taught me something: sometimes the old ways are right because they're simple. MIDI is simple. The browser isn't. Respect the gap.


Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.

Free · No Commitment · 48-Hour Delivery

Get a free infrastructure audit

2-hour remote session. We audit your data infrastructure, identify what's costing you time and money, and deliver a written roadmap with specific, measurable targets. No pitch.

Book Your Free Audit
N
Nishaant Dixit
Founder & Lead Engineer at SIVARO

Building data-intensive systems since 2018. 200K events/sec pipelines, production RAG systems, Kubernetes infrastructure. LinkedIn →

Start a Project
Need help with your infrastructure?

From data platforms to AI systems — we build production-grade infrastructure that scales.

Explore Our Services