## Wrapping C++ and Python libraries for JavaScript ### The guerilla-slides-jsnation-2025
JavaScript has become the new universal language - and it happened almost by accident * because of all the R&D that went into highly efficient interpreters * because it allows to share backend and frontend code
JavaScript is notoriously difficult to interoperate with other languages. So, for the last two decades, people have been trying to rewrite all existing software in JavaScript - there was simply no other way around it. ## But is this actually feasible?
Today, one can even find a ready-to-use fully functional PNG decoder and encoder as `npm` package - entirely in JavaScript - compatible with both the browser and Node.js! However in the C++ world we have ImageMagick - 30 years of experience in implementing state of the art image manipulation algorithms.
I have authored or co-authored the bindings of four major C++ libraries for JavaScript: * `GDAL` - only Node.js * `ImageMagick` - both browser and Node.js, the first SWIG JSE project, about 2 man-months * `ffmpeg` - only Node.js - the first `nobind17` project, * `PROJ` - both browser and Node.js Rewriting these in JavaScript is a monumental task.
Automatically generating wrappers for high-level interpreted languages by compiling the C/C++ header files is a solved problem: it is called # SWIG and it exists since the 1990s. However until 2024, JavaScript support was very rudimentary, covered only Node.js and wasn't really updated since the Node.js 0.x era.
## The Holy Grail of JavaScript/C++ interoperability ```shell git clone https://github.com/ImageMagick/ImageMagick abracadabra ImageMagick ``` then ```js import { Magick } from '@abracadabra/ImageMagick'; ``` And it works both in Node.js and in the browser!
This has been achieved for Python libraries! It is called `pymport`: ``` npm install pymport npx pympip3 install numpy ``` then ```js const { pymport, proxify } = require('pymport'); const np = proxify(pymport('numpy')); const a = np.arange(15).reshape(3, 5); const b = np.ones([2, 3], { dtype: np.int16 }); ```
### ...For C++ we are not there yet *but at least we are going in the right direction* Today, there are two choices: ### SWIG JavaScript Evolution - Very powerful, supports both Node.js and WASM but has a very steep learning curve ### `nobind17` - `pybind11`-like - Very simple to use, does not require any special knowledge beyond C++ but it has its limits and supports only C++ for Node.js
* `GDAL`, my first project, as a co-author, was completely hand-written bindings - it costed more than 1 man-year * `ImageMagick`, the first SWIG JSE project, took about 1 man-month, 400k lines of C++ code generated from 800 lines of SWIG JSE code * `ffmpeg`, the first `nobind17` project - about 2 man-weeks to get it to mostly working status * `PROJ`, the second SWIG JSE project - about 2 man-weeks The cost savings are tremendous!
* The bindings support both synchronous and asynchronous calls - both in Node.js and in the browser *(async in the browser requires COOP/COEP)* * `import`ing is completely automatic - just as if this was JavaScript - the bundler picks up either the browser WASM version or the Node.js native version * The TypeScript types are autogenerated * The C++ STL is replaced by methods that have native JavaScript feel
Interfacing the two languages is not enough! End-users expect `npm` packages - which must be so seamless, that the developer does not need to know about the C++ *time to ditch `node-gyp`* ## Enter the `hadron` build system
# `hadron` * `meson`-based build system which is compatible with CMake and autotools * `xpm`-orchestrator for seamless integration with `conan` and a built-in C++ compiler in an `npm` package ImageMagick comes prebuilt with more than 20 libraries - on Linux, macOS and Windows, and WASM - and it can rebuild itself upon installation, pulling the libraries from `conan` and using its own bundled C++ compiler!
How about doing this the other way around? Transparent interaction with JavaScript from C++? ```cpp try { Napi::Platform platform; Napi::PlatformEnv env(platform); Napi::Function require = env.Global().Get("require").As
(); Napi::Object axios = require({Napi::String::New(env, "axios")}).ToObject(); Napi::Promise r = axios.Get("get").As
() .MakeCallback(env.Global(), {Napi::String::New(env, "https://www.github.com")}) .As
(); } catch (const Napi::Error &e) { fprintf(stderr, "Caught a JS exception: %s\n", e.what()); return -1; } ```
This was one of the most requested Node API features - ability to `require` and `import` `npm` modules from C++. I created `libnode` in 2023 as Google Summer of Code project - right before the start of the open hostilities between me and Google. Alas, the PR is currently frozen as I was blocked from accessing the Node.js repositories in September 2023 after refusing to stop talking about the French police involvement in my affair. It does not work beyond Node 18.x.
The project is currently available as an Ubuntu package but has not been updated since Ubuntu 22.04. It works reasonably well and it is used by at least one other opensource project. It is targeted mostly at server-side C++ applications which must support user JavaScript plugins. It features automatic `Promise` resolution from C++ and supports loading and calling both synchronous and asynchronous JavaScript functions from CJS and ES6 modules.
I am unemployed and homeless software engineer living on the street on social welfare and working full-time on open source in my van using solar panels. I am currently the target of a huge extortion, that involves many of the largest IT companies in the world - including Google, X, Microsoft and Facebook - as well as the French police - about a series of irregular criminal and civil cases with sexual motivation. After covering up false rape charges against me, my employers trying to convince me that I suffer from psychosis using subliminal messages with sexual content at my workplace.
I chose to live on the street with almost no money instead of accepting a job in a criminal company willing to help cover up this affair since I consider this to be a more meaningful use of my life. I want to live in a society where employers are not allowed to use the police and the criminal justice system for extortion, where the power of the state and commercial interests stops at the doorstep of your home and where your own sexual life is no one else's business.
* SWIG JSE - https://github.com/mmomtchev/swig * `nobind17`- https://www.npmjs.com/package/nobind17 * `hadron` - https://github.com/mmomtchev/hadron * `magickwand.js` - https://www.npmjs.com/package/magickwand.js * `gdal-async` - https://www.npmjs.com/package/gdal-async * `node-ffmpeg` - https://www.npmjs.com/package/@mmomtchev/ffmpeg * `proj.js` - https://www.npmjs.com/package/proj.js * `libnode` - https://github.com/mmomtchev/libnode
# For great justice!