Skip to content
Tech News
← Back to articles

How do you keep Web MIDI from crashing a 1983 synthesizer?

read original more articles
Why This Matters

This article highlights the challenges of integrating modern Web MIDI technology with vintage 1980s synthesizers, emphasizing the importance of careful data flow management to prevent hardware crashes. It underscores the ongoing need for innovative solutions to bridge legacy hardware with contemporary digital interfaces, benefiting both developers and vintage synth enthusiasts.

Key Takeaways

Why writing Web MIDI code for 8-bit CPUs from the 1980s is an absolute timing nightmare, and how to safely control data flows on vintage hardware directly from your browser.

Modern browsers run fast. Your system processor operates in gigahertz, handles multi-threaded operations, and loads gigabytes of data in milliseconds.

The microprocessor inside a vintage 1983 Yamaha DX7 is an 8-bit Hitachi 6305 running at a clock speed of 2 MHz, with a tiny 256-byte RAM buffer.

When you try to bridge these two eras using the modern Web MIDI API, you run headfirst into a classic retrocomputing bottleneck: buffer overflow. Send data too fast, and the synthesizer’s CPU hangs, drops packages, or corrupts the internal sound memory completely.

1. The Death of Flow Control (and the $5 Cable Problem)

In the 80s, MIDI physical hardware operated over a current loop running at 31,250 bits per second. While slow, the bandwidth was constant and predictable.

Today, most computer music setups use modern USB-to-MIDI adapters. Modern computers send USB packets at lightning-fast speeds. A cheap, bufferless adapter receives the data at high USB bandwidths, and instantly attempts to serialize and dump it down the MIDI out pin.

Because standard MIDI lacks hardware handshaking lines (no RTS/CTS pins), there's no physical way for the DX7 to tell the browser: "Hey, stop sending data, I'm writing the last preset block to SRAM right now."

To solve this in JavaScript, we have to enforce a custom software flow throttle:

// Chunking and pacing SysEx transmission arrays async function sendSysExWithPacing(midiOutput, rawSysExBytes) { const CHUNK_SIZE = 256; // Limit blocks to prevent buffer floods const INTER_CHUNK_DELAY = 60; // Milliseconds to wait between packets for (let i = 0; i < rawSysExBytes.length; i += CHUNK_SIZE) { const chunk = rawSysExBytes.slice(i, i + CHUNK_SIZE); midiOutput.send(chunk); // Wait to allow the vintage 8-bit CPU to write block to EEPROM await new Promise(resolve => setTimeout(resolve, INTER_CHUNK_DELAY)); } }

... continue reading