Create music with math. Math2Wav renders a mathematical formula into an audio waveform and writes it to a WAV file. Formulas are written in SSF (Sound Source File), a tiny interpreted math language that supports constants, user-defined functions, imports, and a full set of audio-oriented builtins (oscillators, envelopes, filters, FX).
Both a command-line renderer (math2wav) and an interactive GUI (math2wav-gui) are provided. The GUI is a live-coding environment: edit the formula and the sound updates in real time.
- Formula to WAV: evaluate
f(x)for every sample and write 8/16/32/64-bit PCM (or 32/64-bit IEEE-754 float) WAV files. - SSF language:
constvariables,fnfunctions, multi-fileimport, comments, and a library of builtins. - Band-unlimited oscillators:
saw,tri,sqr, deterministicnoise, plus standard math functions. - Live GUI: Nuklear-based editor with real-time audio via miniaudio, hot-reload on every edit, syntax diagnostics with squiggly-line support, play/pause, gain, and one-click WAV export.
- Structured diagnostics: parse/compile errors are reported with line/column info, usable both on the CLI and in the editor.
- Extensible audio backend: a generator-based audio API (
formats/audio.h) currently ships a WAV backend and is designed for more formats.
- CMake >= 3.16
- A C23 compiler (GCC 13+, Clang 16+, or similar)
- SDL2 (fetched as a Git submodule)
- The SDL2, GLAD, tinyexpr, Nuklear, and miniaudio sources are vendored under
src/thirdparty.
# 1. clone with submodules (or run these after cloning)
git clone --recurse-submodules https://github.com/f1nel-egy/Math2Wav.git
cd Math2Wav
git submodule update --init --recursive
# 2. configure and build
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -jThis produces two executables in build/:
| Executable | Purpose |
|---|---|
math2wav |
Command-line renderer: .ssf file to .wav |
math2wav-gui |
Interactive live-coding editor (SDL2/OpenGL) |
Usage: math2wav [OPTION]... [FILE.ssf]
Render a sound from a formula and write it to a WAV file. The formula can come
from a Sound Source File (.ssf), the -m option, or, if neither is given, a
built-in default tone.
| Option | Description |
|---|---|
-h, --help |
Show help |
-v, --version |
Show version |
-o, --output FILE |
Output WAV file (default output.wav) |
-i, --input FILE |
Read settings and formula from an .ssf file |
-m, --math FORMULA |
Use FORMULA directly (overrides the .ssf formula) |
-f, --frequency FREQ |
Sample rate in Hz |
-d, --duration DURATION |
Duration in ms |
-w, --width WIDTH |
PCM bit width in bits (8, 16, 32 or 64) |
Examples:
math2wav examples/sine.ssf -o sine.wav
math2wav examples/testsong.ssf -o testsong.wav
math2wav -m "0.25 * sin(2 * pi() * 440 * x)" -d 2000 -o beep.wavRun math2wav-gui to open the live-coding editor:
- Edit SSF source in the editor pane; the sound recompiles and updates while you type (250 ms debounce).
- Play/Pause toggles real-time playback, Gain controls output level.
- File field + Load opens an
.ssffile (imports resolve relative to it). - Export .wav renders the current source to a WAV file.
- The diagnostics pane lists errors/warnings with line numbers; clicking one jumps to the line.
- When compilation fails, the audio engine keeps playing the last good version.
SSF (Sound Source File) is a tiny math language interpreted at runtime. It is
built on tinyexpr and adds user-defined
symbols, imports, and audio helpers. The variable x is always time in
seconds; the result should stay in [-1, 1] (anything outside is clamped).
Top-level key = value lines configure the render:
samplerate = 48000
duration = 2000
width = 32
format = wav
Accepted aliases: samplerate/sample_rate/rate/hz, duration/ms,
width/bits/bit_width, format/container. width supports 8, 16, 32 or
64 bits.
const TAU = 2 * pi()
const BPM = 128
fn eq(a, b) = 1 - sign(abs(a - b))
fn kick(t) = sin(TAU * 50 * t) * exp(-t * 8)
import "lib/notes.ssf"
import "lib/env.ssf"
Imports are resolved relative to the importing file and are de-duplicated.
A standard library lives in examples/lib:
| File | Contents |
|---|---|
lib/notes.ssf |
Note frequency constants C0…B8, midi(), semis() |
lib/time.ssf |
BPM / beat / bar / step / gate helpers |
lib/env.ssf |
Envelopes (decay, ar/ad, adsr, lfo, …) |
lib/fx.ssf |
Distortion, clipping, filtering, tilt, mixing helpers |
Arithmetic: + - * / ^ % (% is fmod, ^ is power)
Math: sin cos tan asin acos atan atan2 sinh cosh tanh exp ln log log10 pow sqrt abs floor ceil fac
Audio:
| Function | Description |
|---|---|
fmod(a, b) |
Floating-point modulo |
min(a, b) |
Minimum |
max(a, b) |
Maximum |
clamp(v, lo, hi) |
Clamp between lo and hi |
sign(a) |
Sign (-1/0/1) |
saw(p) |
Band-unlimited sawtooth, one cycle per unit of phase |
tri(p) |
Band-unlimited triangle, one cycle per unit of phase |
sqr(p) |
Band-unlimited square, one cycle per unit of phase |
noise(seed) |
Deterministic white noise in [-1, 1] |
pi(), e() |
Constants |
# this is a comment
// so is this
Every example is a complete .ssf file in examples/:
| File | What it demonstrates |
|---|---|
sine.ssf |
A simple 440 Hz sine tone |
beat.ssf |
A kick-drum-style beat in a one-line formula |
gabber.ssf |
200 BPM gabber kick + hoover bass |
house_drums.ssf |
Full house drum machine (kick, clap, snare, hats, rim) |
testsong.ssf |
A complete 136-second track with arrangement + sidechain |
rcs_helmet_vibration.ssf |
RCS thruster / helmet vibration sound design |
Render one with:
math2wav examples/testsong.ssf -o testsong.wavMath2Wav
├── CMakeLists.txt # CMake build (C23)
├── examples/ # .ssf files + lib/ standard library
└── src/
├── main.c # CLI renderer
├── maingui.c # GUI entry point (SDL2 + OpenGL + Nuklear)
├── libs/
│ ├── math2wav.* # shared math/audio helpers
│ ├── ssf.* # SSF language: parser, compiler, evaluator
│ ├── m2waudio.* # real-time playback via miniaudio
│ └── m2wui.* # Nuklear-based editor UI state + drawing
├── formats/
│ ├── audio.* # pluggable audio file generator API
│ └── wav/* # WAV backend implementation
└── thirdparty/ # SDL2 (submodule), glad, tinyexpr,
# nuklear, miniaudio
- SDL2 (submodule,
branch = SDL2) - windowing/input - glad - OpenGL loader
- tinyexpr - expression parsing/evaluation
- Nuklear - immediate-mode GUI
- miniaudio - audio playback
This project is licensed under the GPL 3.0 - see LICENSE.
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.