Vult: a transcompiler that turns DSP functions into portable C, JavaScript or Pure Data externals
Vult is a transcompiler well suited to write high-performance DSP code
At a glance
- What is it?
- Vult is a small language for writing audio effects and synth code that compiles to plain C, fixed-point C, JavaScript or Pure Data externals, with per-instance memory as the core abstraction. It suits a DSP developer targeting microcontrollers without an FPU, and it is written in OCaml and shipped as a native binary, an npm package or a browser library.
- Who is it for?
- Vult fits a DSP developer who writes one algorithm and needs it to run in several places, on a Teensy or Pico without an FPU, in a browser, or as a Pure Data external, and it does not fit a general-purpose language user, because the whole language is built around one idea: a function with its own memory.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly OCaml, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A transcompiler, and what that word means here
Vult is described as a small language to program high-performance algorithms that may run in small microprocessors or microcontrollers, and it is specially useful when programming Digital Signal Processing algorithms like audio effects or synthesizers.
The key architectural word is transcompiler. The Vult compiler takes Vult code and produces plain C/C++ code that can be compiled in any platform with a C/C++ compiler, for example Arduino or Teensy boards using fixed-point arithmetics. It can also generate JavaScript that runs directly in the browser, or C/C++ that can be compiled as Pure Data externals.
That is a different design from a compiler that targets its own bytecode or its own runtime. The output is ordinary C that you compile with the toolchain you already have for the target board, and the readme is explicit that you need to include two files from the runtime, runtime/vultin.h and runtime/vultin.cpp, in your project. The generated code is therefore not standalone; it is a C file plus a small support library you link.
The payoff is portability of the source rather than of the binary. An algorithm written once in Vult can be emitted as floating-point C for a desktop plugin, as fixed-point C for a microcontroller, as JavaScript for a browser instrument, or as a Pure Data external, and the DSP logic does not change.
The compiler itself is written in OCaml, and the readme says the code written in Vult has freedom, which is the project's way of saying the language stays small and the compiler stays readable. The primary language is OCaml, the homepage is vult-dsp.github.io/vult, the default branch is master, and the last push was on 2026-10-17.
The release history is worth noting, because it is irregular. The most recent listed release is v1-latest, a pre-release dated 2026-03-10, and before that come v0.4.15 from 2023-02-25 and v0.4.14 from 2022-12-02. So the versioning jumped from a 0.4 line to a 1.0 line that is still labelled a pre-release, which is worth considering if you are pinning a version.
Compiling to C, and the fixed-point switch
The command line entry point is vultc, and the two forms you will use are short.
To generate C/C++ code with floating point arithmetic, run vultc with the -ccode option.
$ vultc -ccode infile.vult -o outfileThat produces outfile.h and outfile.cpp. In order to compile and link these files you need to include in your project the files runtime/vultin.h and runtime/vultin.cpp, which is the runtime support layer the generated code calls into.
For fixed point, the command adds -real fixed.
$ vultc -ccode -real fixed infile.vult -o outfileFixed point arithmetics are performed in q16 format, 16 bits for the integer part and 16 for the decimal.
The reason to care is stated later in the readme. The Vult compiler can generate C++ code that performs all the calculations using fixed-point arithmetics, and fixed-point executes faster than floating-point in processors that do not have a dedicated FPU. The named examples are AVR Arduinos, Teensy 3.2 and Raspberry Pi Pico.
That is the core use case for the language, and it explains the rest of the design. A Teensy 3.2 has a single-precision FPU, an AVR has none, and a Pico has none either in its RP2040. Writing DSP for those targets in plain C means hand-managing fixed-point scaling, overflow behaviour and Q formats at every operation, which is where most of the effort goes and where most of the bugs live. Doing it in Vult means writing the algorithm in floating point and letting the compiler do the conversion.
Vult also provides a few templates, for example to generate objects compatible with the Teensy Audio Library or Pure Data externals, so the generated code fits an existing host's expectations rather than requiring a wrapper you write yourself.
A function with its own memory is the whole language
The readme describes the central idea: a single function can be a full processing unit, to be thought of as a block or a box in a graphical audio environment, for example a box in Pure Data. Functions can store information.
The example is a digital biquad filter.
fun biquad(x0, b0, b1, b2 ,a1 ,a2) : real {
mem w1, w2;
val w0 = x0 - a1 * w1 - a2 * w2;
val y0 = b0 * w0 + b1 * w1 + b2 * w2;
w2, w1 = w1, w0;
return y0;
}That function declares two memory variables, w1 and w2, which will remember the state of every instance of this filter. So mem is the state store and it is per-instance, not per-call.
The consequence is shown in the next example, where a process function calls biquad twice.
fun process(i1, i2) {
val o1 = biquad(i1, ....);
val o2 = biquad(i2, ....);
}The filter that processes the signal i1 is completely independent of the one that processes i2. In a language where a function's local variables die when it returns, this is not possible without allocating state explicitly and passing it around. Here the mem declaration is enough, and each call site becomes an independent instance.
That is the abstraction the whole language is built on, and it maps directly onto how DSP is actually structured. A filter is a stateful box, a synth voice is a stateful box, and a graph is boxes wired together. Writing DSP in a language where state has to be threaded manually as a parameter means the code reads nothing like the signal flow it implements.
The language also has compile-time lookup tables and embedded wav files, with and without interpolation. The example given replaces a function body with a lookup table that would be more efficient than executing the body.
fun pitchToRate(d) @[table(size=127,min=0.0,max=127.0)] {
return 0.00018539226566085504 * exp(0.057762265046662105 * d);
}The annotation turns the function into a table lookup with a stated size and range, which is the kind of thing you would otherwise write by hand in the generated C.
Three ways to get the compiler, and one is much slower
There are three flavors of the Vult compiler, and the readme is candid about the performance difference between them.
The first flavor is the command line, available as native executables from the releases page and as a node.js version on npm. The second is a node.js library called vultlib. The third is a web browser library.
Installing the JavaScript version through npm takes one command.
$ npm install vult -gThat installs the vultc command in your path, and the same command updates Vult, which matters because the readme says Vult is updated frequently.
The performance caveat is stated plainly: this version of the compiler is 4x slower than the native version, and if you have a large project it is recommended to install the native compiler. Four times slower is a large factor for a compiler that a build step will invoke, and it is the kind of detail that is easy to miss until a project build starts feeling slow.
The native executables can be downloaded from the releases page; you pick the executable for your operating system and place it in a location pointed to by your PATH variable.
The library flavor installs with npm install vultlib, and the browser flavor is a single script tag.
<script src="https://modlfo.github.io/vult/javascripts/vultweb.js"></script>Embedding the compiler in a web page gives you the full functionality in the browser, and the WebAudio example repository shows what that is for: a DSP tool that runs entirely client-side, where the code is compiled in the page rather than fetched as a prebuilt binary.
For editor support there is a Sublime Text syntax file, which goes into the User packages directory, and a Visual Studio Code extension named vult available in the marketplace.
Building from source, and the OCaml toolchain behind it
Compiling Vult from source needs Opam and a working OCaml toolchain, and the readme recommends Linux or Mac, noting that it is possible to compile on Windows but the process is more difficult.
Opam installs from the package manager on Debian and Ubuntu.
$ sudo apt install opamAnd on Mac through brew.
$ brew install opamThe Makefile shows what the build actually involves. The compiler target builds both a native and a bytecode executable of vultc with ocamlbuild, using -j 4 and -use-ocamlfind, and it depends on the version and format targets first.
The format target is interesting because it degrades gracefully. The Makefile checks whether ocamlformat and ocp-indent are on the PATH, and if they are not, it substitutes echo for them, so a missing formatter does not break the build. The repository also carries .ocamlformat and .ocp-indent configuration files plus a .merlin file, which is the older OCaml project configuration.
The other targets are the reason this project can ship the same compiler in four forms. js builds the node.js library by compiling vultlib.byte and running it through js_of_ocaml with --target-env=nodejs. jscompiler does the same for the browser compiler and writes the result to vultc.js with a node shebang and the executable bit set. web produces the browser library with --target-env=browser. And vultc_h embeds the browser compiler into a C header as vultc.h, which is how a native program can call the JavaScript compiler.
The test targets are more than a pass or fail. The test target builds a native test binary and runs it with a sequential runner and one shard. test-fast adds the internal flag. test-update passes -update true, which is the mechanism for accepting new expected output. And coverage runs the tests and the perf binary under BISECT_COVERAGE, then sends the report to Coveralls with bisect-ppx-report.
The perf target builds and runs test/perf.native, so performance is a measured part of the test suite rather than an afterthought.
The top-level entries also include the runtime/ directory with the support library the generated C needs, examples/ with subdirectories for effects, env, filters, midi, osc, units, util and web, and an other/ directory holding the editor syntax files and images.
Editorial conclusion
Vult fits a DSP developer who writes one algorithm and needs it to run in several places, on a Teensy or Pico without an FPU, in a browser, or as a Pure Data external, and it does not fit a general-purpose language user, because the whole language is built around one idea: a function with its own memory. Before adopting it, confirm the target toolchain can build the generated C, since you need runtime/vultin.h and runtime/vultin.cpp linked into your project, and pick the fixed-point variant early if your hardware lacks a floating-point unit.
Frequently asked questions
What is Vult and what does it compile to?
Vult is a language for writing DSP algorithms such as audio effects and synthesizers. Its compiler is a transcompiler that produces plain C/C++, which can be floating point or fixed point, JavaScript that runs in the browser, or C/C++ that compiles as a Pure Data external.
How do I compile a Vult file to C?
Run vultc -ccode infile.vult -o outfile, which produces outfile.h and outfile.cpp. You then need to include runtime/vultin.h and runtime/vultin.cpp in your project so the generated code links against the runtime.
What does the mem keyword do in Vult?
It declares memory variables that remember the state of every instance of the function. A biquad with two mem variables has independent state per call site, so two calls in the same function produce two independent filters.
Which platforms can I run Vult-generated code on?
Anything with a C or C++ compiler. The readme names Arduino and Teensy boards using fixed-point arithmetics, with fixed-point code executing faster than floating point on processors without a dedicated FPU, giving AVR Arduinos, Teensy 3.2 and Raspberry Pi Pico as examples.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vult-dsp-vult)Was this useful?