DEV Community

Cover image for YM2149 in Rust, Part 2: CLI Player and Browser Demo
Markus Velten
Markus Velten

Posted on

YM2149 in Rust, Part 2: CLI Player and Browser Demo

From terminal oscilloscopes to WebAssembly in 150KB


This is Part 2 of a 4-part series on building a complete YM2149 chiptune ecosystem in Rust. In Part 1, we covered the chip itself and the cycle-accurate emulator core. Now let's look at the tools built on top of it.


The Terminal Player

Before building anything graphical, I wanted a simple way to test playback. The CLI player started as a debugging tool and grew into something I actually enjoy using.

CLI player with Playlist support

The CLI player supports YM, SNDH, AY and AKS songs, with real-time oscilloscope, spectrum analyzer, and a searchable playlist browser.

CLI player playlist replay

The CLI player with spectrum bins and oscillators for visuals

The interface shows:

  • Metadata: Song title, author, format, duration
  • Oscilloscope: Per-channel waveform display (those aren't just pretty — they help debug envelope timing issues)
  • Spectrum analyzer: Frequency content of each channel
  • Playback controls: Keyboard shortcuts for mute, pause, volume, navigation

The playlist browser pulls from a local music directory and displays format tags — [SNDH], [AY], [YM1-6/YMT1/2], [AKS] — so you can see at a glance what you're loading. Type to search, arrow keys to navigate, Enter to play.

CLI standalone replay

Playing "Wings of Death 6" by Jochen Hippel — a YM5 format file with digi-drum effects.

# Install the CLI globally
cargo install ym2149-replayer-cli

# Browse a directory
ym2149-replayer ./chiptunes/

# Play a single file
ym2149-replayer ./examples/ym/ND-Toxygene.ym
Enter fullscreen mode Exit fullscreen mode

The status line at the bottom shows per-channel state: tone (T) or noise (N), amplitude, frequency, and note name. When a channel plays a digi-drum sample, it shows [DRUM] instead of a frequency. These details matter when you're trying to figure out why a particular track sounds different from the original hardware.

Arkos Tracker supports multiple YM2149 chips in one song — something that makes it a unique soundchip editor. The CLI handles multi-PSG files transparently.


Browser Playback in 150KB

WebAssembly compilation was almost anticlimactic. Rust's wasm-pack tool handled most of the work. The tricky part was the audio API.

Web Audio requires you to run code in an AudioWorklet — a separate thread with strict timing requirements. The WASM module exposes a simple interface:

const player = new Ym2149Player();
player.load(arrayBuffer);  // YM, SNDH, AY, or AKS
player.play();

// In the AudioWorklet
process(inputs, outputs) {
    const samples = player.generate_samples(128);
    outputs[0][0].set(samples);
    return true;
}
Enter fullscreen mode Exit fullscreen mode

The compiled WASM weighs about 150KB — smaller than most MP3 files, yet it contains complete emulation of the YM2149, Z80 CPU (for AY files), and Motorola 68000 (for SNDH files).

The browser demo supports loading your own song files (AY, SNDH, YM, AKS) besides providing some examples for each format. The UI shows metadata, playback progress, and per-channel mute toggles.


Performance Characteristics

Numbers matter. Here's what to expect:

Metric Value
CPU per active player ~2-5% of one core @ 44.1kHz
Memory per player ~1-2MB (mostly frame data)
Latency (native) ~20ms with default buffer
Latency (WASM) ~40-80ms depending on browser
WASM binary size ~150KB (gzipped: ~60KB)

The hot path — clock() and get_sample() — runs 44,100 times per second per player. It's optimized for this: no allocations, no branches in the inner loop, cache-friendly data layout. On modern hardware, you could run dozens of simultaneous players before audio becomes a bottleneck.

For comparison, decoding a compressed audio format like Vorbis or MP3 typically uses similar CPU but requires megabytes of audio data to be loaded into memory. The YM2149 approach trades decode complexity for generation complexity, and the generation is simpler.


What's Next

In Part 3, we'll integrate with the Bevy game engine — playlists, crossfading, sound effects, and a demoscene-style visualization example.


Try It

Top comments (0)