Skip to main content
Back to articles
2026 / 01
| 3 min read

Storage Peripherals and Audio Realism

Floppy, hard drive, and cassette peripherals with Kansas City Standard encoding, plus a peripheral audio bus that makes the machine feel physical.

emulator peripherals cassette audio kcs

The hard drive clicked, the floppy head swept, and the cassette motor hummed, and none of them sounded like they were in the same room. Each device had its own volume and its own frequency response, because each one had been wired to the speakers on its own terms. The audio path was a pile of one-off connections.

So I flattened it into a bus system:

Generators → Buses → Master Chain → Destination

There is now a dedicated peripheral bus with an 8 kHz lowpass filter. Stepper motors and plastic cassette decks don’t sound like full-range synthesizers, so the bus enforces a shared timbre before anything reaches the master chain. The PeripheralAudioHelper class connects devices to the bus and standardizes the verbs: playTone(), playSweep(), playClicks(), startMotor().

The floppy, hard drive, and cassette all speak the same audio protocol now. Volume control is global rather than device-specific, and the mute toggle actually mutes.

Hard drives with geometry

The hard drive peripheral models geometry and capacity, and both feed back into behaviour. There are four variants:

  • Seagate ST-225 (20MB MFM, 1984)
  • Seagate ST-4096 (80MB MFM, 1987)
  • Conner CP-3104 (120MB IDE, 1991)
  • WD Caviar 2540 (540MB EIDE, 1995)

Geometry gives the emulator something to own: cylinders, heads, sectors per track. Format operations take longer on larger drives, and seek sounds are tuned per drive, so switching drives in the peripheral shop is audible.

Cassette as a real transport

The cassette deck is a Kansas City Standard recorder with a transport model behind it. It moves tape, tracks position, and stores programs with a baud rate and a checksum. The encoder is explicit about its constraints:

  • Mark (1) = 2400 Hz
  • Space (0) = 1200 Hz
  • Baud rates = 300 / 1200 / 2400

Start the motor, stream FSK, advance position, stop at the end of data. It can go wrong the way tape does, and the machine remembers where you left off.

Boot theatre

The BIOS boot sequence now plays mechanical sounds when it “detects” owned hardware. That is not ROM BIOS code, it is the frontend boot path, but it does make the box feel occupied. A present floppy performs a head calibration sweep; a present hard drive spins up and clunks through a few seeks. Both route through the peripheral bus, so the boot sits in the same acoustic world as everything after it.

Wrong numbers got a voice

I also expanded the wrong-number system into six categories with 20 variations each. The line can now land on a busy signal, a calm operator, an annoyed person, or a random IVR system. Those announcements are audio files routed through the line bus with its own phone-line filter, so they sit alongside DTMF and handshake tones rather than on top of them.

Two bugs from the edge cases

A Z-Machine branch offset was off by one. The game mostly worked, which took far longer to notice than to fix.

Audio context initialization in Lit components has to happen after a user gesture, because the browser enforces it. I moved the storage peripherals to async audio initialization so the sounds don’t try to play before the user has clicked something.

Tomorrow: finishing the CC-40 backend and launching the IVR system properly.

Previous: 2026-01-28