Semitone — an embeddable scripting language

After 25 years of writing DSP algorithms, I’ve gotten tired of the same loop: prototype in MATLAB, rewrite in C++ for speed, then spend weeks chasing subtle differences between the two. Every rewrite is a chance to introduce bugs that didn’t exist in the original. So I’ve started building the tool I wish I’d had all along.

semitone is a small interpreted language based on a subset of Octave syntax (pun totally intended…), designed specifically for embedding in audio software. Pure C++17, no dependencies, designed for real-time safe evaluation of certain types of code. The goal is that you can prototype your DSP in semitone, and the same code runs inside your plugin, either by running the interpreter or by transpiling to C++ when I build that feature – in both cases without any manual rewriting.

The interpreter is now at v0.1 and will be released on GitHub soon. Currently working: matrices, indexing, basic linear algebra, control flow (for, if/else), function definitions, and a growing set of built-ins (trig, linspace, hadamard, disp, num2str, etc.). Not yet there: JUCE integration, user-defined function, C++ code generation. But those are on the to-do list. Actually there’s a roadmap on the website.

I’ve put up two interactive demos on the website so people can actually try the language without me having to ship binaries first — the whole interpreter is compiled to WebAssembly (about 2 MB) and runs entirely client-side in the browser:

https://semitone.om

One demo walks through the syntax and built-ins; the other generates SVG output (the semitone logo on the site was generated by a semitone script).

A few things worth knowing about the longer-term plan:

  • The interpreter itself will be released under a permissive license once it’s ready for external eyes.

  • Tools and plugins built on top (e.g. a synth where you define your signal chain in semitone) will be commercial products.

  • One existing feature worth mentioning: a chunked-scalar evaluation mode that gets certain types of DSP code to within 2-3× of -O3-compiled C++. The website does explain this in more detail.

What I’m hoping to get from posting here: early feedback before the major design changes become too costly. The JUCE community is probably the most concentrated group of people who’d actually use a tool like this, and you’d see issues in the design that I might only discover after committing to them publicly.

Any reactions welcome — particularly around the language subset choices (what’s missing? what’s redundant?), and what you’d want from a JUCE integration when that lands. So far I got a wrapper around the semitone tokenizer that does the job of a CodeTokeniser for CodeEditorComponent.

Happy to discuss the architecture, the WASM build, the interpreter internals, or anything else.

Ah but do array indices start at 0 or 1?

1 Like

For compatibility reasons, at 1. But I know it is one of the more annoying features of Octave/Matlab… And I’m thinking of putting a “0-based/1-based” toggle. But to be honest, with most workflows you probably want to be able to switch back and forth between semitone and Matlab/Octave. So there’s not really a choice, even if the above-mentioned toggle.