Show HN: Oworpheus – a tiny music programming language

nan601 pts0 comments

cmt/oworpheus: a tiny music programming language - Forgejo: If it works, we don't run it

This website requires JavaScript.

cmt/oworpheus

Watch

Star

Fork

You've already forked oworpheus

Code

Issues

Pull requests

Projects

Releases

Packages

Wiki

Activity

Actions

a tiny music programming language

3 commits

1 branch

0 tags

93 KiB

Rust

100%

main

Find a file

HTTPS

Download ZIP<br>Download TAR.GZ<br>Download BUNDLE

Open with VS Code

Open with VSCodium

Open with Intellij IDEA

Exact

Exact

Union

RegExp

cmt

77c6186ea4

Update SPEC.md

2026-06-10 20:58:50 -07:00

examples

Initial commit

2026-06-10 20:55:23 -07:00

src

Initial commit

2026-06-10 20:55:23 -07:00

.gitignore

Initial commit

2026-06-10 20:55:23 -07:00

Cargo.lock

Initial commit

2026-06-10 20:55:23 -07:00

Cargo.toml

Initial commit

2026-06-10 20:55:23 -07:00

LICENSE

Initial commit

2026-06-10 20:55:23 -07:00

README.md

Initial commit

2026-06-10 20:55:23 -07:00

SPEC.md

Update SPEC.md

2026-06-10 20:58:50 -07:00

README.md

oworpheus

A tiny music programming language. Write .owo source files in a C-like syntax,<br>compile them straight to .wav.

cargo build --release<br>target/release/oworpheus examples/demo.owo<br># wrote examples/demo.wav (4.66s @ 44100 Hz)<br>examples/ also has two simple chiptune covers to play with: mario.owo<br>(Super Mario Bros. overworld, square lead + triangle bass) and zelda.owo<br>(Zelda overworld theme, detuned saw "brass").

Language

The normative language definition is SPEC.md; this<br>section is a working overview.

An .owo file has four kinds of top-level items: tempo, instrument,<br>track, and play. Comments are // and /* ... */.

tempo 110;

instrument lead {<br>let vib = sine(5) * 4; // 5 Hz LFO, +/- 4 Hz vibrato<br>let body = saw(freq + vib) * 0.5<br>+ sine(freq * 2) * 0.2; // octave-up sine layer<br>out body * adsr(0.02, 0.15, 0.6, 0.3) * amp;

track melody uses lead {<br>note E4 1/2; // pitch, duration in beats<br>note G4 1/2 0.9; // optional velocity 0..1 (default 0.8)<br>chord [C4, E4, G4] 2; // several pitches at once<br>rest 1;

play melody;<br>tempo

tempo ; sets the global tempo. Defaults to 120 if omitted.

instrument

An instrument describes how one note sounds, as a signal expression evaluated<br>per sample. It is a sequence of let bindings followed by exactly one out.

Inside an instrument these names are predefined for the note being played:

name<br>meaning

freq<br>pitch in Hz

amp<br>velocity from the score, 0..1

time<br>seconds since the note started

dur<br>the note's held duration in seconds

Signal generators:

sine(hz), saw(hz), square(hz), triangle(hz) — oscillators. The<br>argument is itself a signal, so oscillators can modulate each other:<br>sine(freq + sine(5) * 4) is vibrato, saw(freq * 1.01) is a detuned layer.

noise() — white noise.

adsr(attack, decay, sustain, release) — envelope, times in seconds,<br>sustain as a 0..1 level. Release plays out after the note's duration ends,<br>so notes ring past their slot in the score. Arguments must be constants.

Expressions support + - * /, unary minus, parentheses, and number literals.<br>let bindings are evaluated once per sample and shared, so a bound noise()<br>or oscillator is a single generator no matter how often it is referenced.

track

track uses { ... } is a sequence of timed events:

note [velocity];

chord [, , ...] [velocity];

rest ;

Pitches are written C4, F#3, Bb2 (A4 = 440 Hz). Durations are in beats<br>and may be fractions (1/2, 3/4) or decimals (0.25).

play

play [, ...]; mixes the named tracks together starting at<br>time zero. With no play statement, all tracks play. The final mix is<br>peak-normalized only if it would clip.

Output

Mono 16-bit PCM WAV at 44100 Hz. oworpheus song.owo writes song.wav;<br>use -o to pick another path.

note oworpheus initial commit play language

Related Articles