Experimenting with QuickJS in my plugin outside of VCV-Prototype


I want to experiment with LuaJIT & QuickJS in my own plugin (I understand the performance implications of using the latter and have no plans to push to the library a polyphonic oscillator that takes 5000% CPU).

I’m building on windows with msys64, using a build of Rack rather than the SDK, and have premake installed (as it’s required to build Prototype).

I tried building VCV-prototype, and built it successfully (make dep then make).

I then tried to copy the relevant bits of its Makefile to mine, and make dep works fine, but I always get the following error when I make:

make: *** No rule to make target 'build/src/LuaJITEngine.cpp.o', needed by 'plugin.dll'.  Stop.

And I get a similar error if I try building QuickJS.

I’m not very familiar with the building process, so I feel I must be missing a step somewhere - but no matter how much I look and try out possible solutions, I don’t understand what Prototype is doing that I’m forgetting to do. Hopefully it’s obvious to someone what I’m doing wrong.

Here’s my makefile on my development/test branch: https://github.com/AriaSalvatrice/AriaVCVModules/blob/86266948046b7a70ee85cd65627f9bd9b00c223c/Makefile

And for comparison, here’s prototype’s: https://github.com/VCVRack/VCV-Prototype/blob/master/Makefile

After further experimentation, commenting out these lines is the key:

SOURCES += src/LuaJITEngine.cpp
SOURCES += src/QuickJSEngine.cpp

I imagine they conflict with the SOURCES += $(wildcard src/*.cpp) included in the default plugin makefile, maybe trying to build something twice. Hopefully those hours wasted on this will save someone time.

Debugging make files must be the least rewarding part of programming, yes? I hate it.


do you have those files in your own source tree to build? looking at your repo, it appears not, so yeah, that’s why it can’t build them.

the reason why those files are included explicitly for prototype is that the engines that get built can be controlled by command-line or environmental settings when running make, so you can turn on or off specific engines.

hope that helps!

1 Like

Ah, from looking at it, it seemed to be toggled by the variables before the make targets. Anyway, things seem to work! (Now for the fun part, figuring out that barely documented JS engine)

your best bet is to not use that as an embedding guide, it was a straight port of Andrew’s duktape engine with all api calls moved to QuickJS. It’s simply an implementation of the prototype engine.

I’m happy to help point you in a good direction if you let me know what you’re trying to do, though!

1 Like

Thanks for helping out on Discord!

As I mentioned over there, I’m pretty much just exploring the potential of importing useful well-tested music libraries that aren’t performance-bound. What’s a daunting project in C++ can often solved with a 10-liner piping javascript libraries together.

Right now my immediate use case is importing Teoria.js to parse textual user input, and eventually make a module that turns a chord progression into something usable in VCV, supporting as many input formats as I can (chord symbols, flat or sharp, slash chords, roman numeral notation, ascii guitar tabs).

I’ll let everyone know how it goes!


(Renamed the thread from “Trouble building LuaJIT / QuickJS in my plugin using Prototype’s method”)

I have QuickJS working with the library I wanted! Thanks for your help in the discord channel, Jerry, saved me a ton of time. I really hoped I could get it to work, because Duktape, which was way easier to get going, only supports ES5, while I wanted to use very recent Typescript libraries.

This is turning my goal of parsing text input from being a C++ project to a Javascript project, which will make it incredibly easier. At a much greater CPU cost for the user, of course, but hopefully still a negligible one given that I don’t plan to do ongoing data processing with JS.

My thinking is to encapsulate QuickJS in the most lightweight way possible: evaluate strings as javascript, read variables. Right now I have it working a bit like this:

Javascript::Runtime js;

js.evaluateString("progression = Tonal.Progression.fromRomanNumerals('C', ['IMaj7', 'IIm7', 'V7'])");
const char* progression = js.readVariableAsChar("progression");
DEBUG("JS progression = %s", progression);
// [6.049 debug src/Test.cpp:37] JS progression = CMaj7,Dm7,G7

js.evaluateString("number = '5' + '0'");
int32_t number = js.readVariableAsInt32("number");
DEBUG("JS number = %d", number);
// [6.049 debug src/Test.cpp:41] JS number = 50

Now… What else is there in NPM that would be fun to encapsulate as a VCV module?

1 Like

So, the experiment was quite a success!

My module can, on user input, spin up a runtime, load a 30Kb minified javascript library, do a bit of fuzzy text parsing, and results are instant.

My implementation is pretty minimal and dirty and wrong in dozens of ways. Evaluate strings, read the contents of variables, use JSON for interchange instead of doing it the more civilized way.

The documentation of QuickJS is mostly “just read the uncommented headers lol”, and there’s barely any example of QuickJS usage in the wild outside of Prototype, so it was much more difficult to get a minimal implementation working than the tiny implementation I have to show for it, but maybe someone will be interested in taking it further? Obviously, I can’t stress enough DSP code should never be written in JS, but it opens the door to all sorts of data processing that is a pain in C++ but a solved problem in the JS world.