E. Lamprecht’s MZ2SYNTH is a delightfully weird piece of code. It is an advanced wavetable synthesizer programmed only by an input image. Here’s an example:
Documentation is pretty sparse, so I’ve had to work it out as best I can:
- input data must be a 720 px high NetPBM PPM or PGM image with a black background
- waveforms are specified by pixel colour: sine, square, sawtooth and triangle are red, green, blue and luminance
- dynamics are manipulated by changing the pixel brightness
- the input plays at a constant rate along the horizontal pixels, defaulting to 10 pixels/second
- The pitch is specified by the Y coordinate. To convert from MIDI note number n to an input coordinate for mz2synth, use this formula:
y=6×(140 – n)
So for Middle C (MIDI note 60), the Y coordinate would be 480.
I’ve created a very simple example that plays a C major scale with simple sine waves with no dynamics.
The input image:

The resulting audio:
And the python code that produced the image:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# mz2-draw - draw a Cmaj scale in the right input format for mz2synth
# scruss, 2025-11
# mz2synth - https://github.com/frankenbeans/MZ2SYNTH
# command line:
# mz2 -v -o mz2-cmaj.au mz2-cmaj.ppm
from PIL import Image, ImageDraw
# convert midi note number (20..127) to
# vertical offset for mz2 input
# notes < 20 (G#0) can't be played by mz2
def midi_to_y(n):
return 6 * (140 - n)
middle_c = 60
maj_scale = (0, 2, 4, 5, 7, 9, 11, 12)
# maj_chord = (0, 4, 7)
# mz2 input must be 720 px high,
# preferably black bg
im = Image.new("RGB", (10 * len(maj_scale), 720), "black")
draw = ImageDraw.Draw(im)
for i, d in enumerate(maj_scale):
# bright red lines mean full
# volume sine waves
draw.line(
[
10 * i,
midi_to_y(middle_c + d),
10 * i + 8,
midi_to_y(middle_c + d),
],
"red",
1,
)
# mz2 can only read NetPBM PPM format
im.save("mz2-cmaj.ppm")
Building
mz2synth comes with Windows and Mac OS binaries. To run the Mac code, you need Homebrew with the gcc@13 recipe. See this issue for details.
To build on Linux, you’ll need gfortran. A build script could be something like this:
git clone https://github.com/frankenbeans/MZ2SYNTH.git
cd MZ2SYNTH/SOURCE
make -f Makefile.gfortran
Put the resulting mz2 binary somewhere in your path, and that’s all the installation it needs. These same instructions should work for Mac OS.
If you really want to live on the edge (note: not really) and get a faster binary at the expense of array bounds checking, use this to recompile instead of the above make line:
rm *.mod *.o mz2
make -f Makefile.gortran.nochk
Leave a Reply