u/JiaHajime

An Example of Nim WASM for Wolfram Alpha alternatives
▲ 32 r/nim

An Example of Nim WASM for Wolfram Alpha alternatives

My original post: https://www.reddit.com/r/sideprojects/s/2WBPjTT8Wu

I’ve been pretty busy lately, but I was thinking—what if Nim had its own SymPy Gamma / Wolfram Alpha-style project?

By using WASM, most of the computation could run directly in the browser, so it could stay free without needing a backend server. The compiler is still the WASM based Nim Compiler in the Web which exports the HTML and Wasm file.

It could start with symbolic math, derivatives, integrals, equations, matrices, and step-by-step solutions, and grow from there.

Basically, a small computer algebra system for Nim + WASM. Link to the calculator: www.qoqoro.online

u/JiaHajime — 7 days ago
▲ 18 r/nim

Update: Trying to add JS Binding with in-Browser Nim Compiler

A few weeks back I posted about getting Nim 2.2.4 to compile and run entirely in the browser prev post, no backend, no native toolchain, no JS fallback. However, I am still interested with using Nim instead of Rust Yew or C Clay to build Web UI.

Now it can. I built a module I called BindWeb, a binding layer that lets the Nim code you compile in-browser drive the actual DOM, Canvas2D, WebGL, WebGPU, Audio, WebSocket, Fetch, localStorage, and input events — and it all still compiles client-side through the same Nim → C → clang.wasmlld.wasm pipeline in browser.

🔗 Live demo: https://benagastov.github.io/bindweb-nim-WASM-compiler/
💻 Source: https://github.com/benagastov/bindweb-nim-WASM-compiler

How the bindings work

WASM can't touch the DOM directly, so BindWeb is built around a tiny command protocol instead of hundreds of individual imports:

  • Nim writes binary command packets (opcode + args) into a shared linear-memory buffer.
  • On flush, the JS runtime walks that buffer and replays each command against the real browser API.
  • DOM nodes / GL objects / audio elements live in JS and are referenced from Nim by integer handles, with a free-list so handles get recycled.
  • Events go the other direction through a second event buffer: JS encodes clicks/keys/fetch results/etc., Nim drains them on the next tick.

The whole API surface (the Nim modules, the JS runtime, and the C runtime) is generated from a single schema.def, so adding a new browser API is a schema entry rather than three hand-written layers that drift apart.

I had to keep the Nim-side references alive so ORC wouldn't collect the object holding a live DOM handle. Once that was sorted, you can create an element in Nim and attach an event listener to it, all Nim-side.

Haven't tried existing Nim JS-binding libs (std/dom, jsffi, Karax) — they target the JS backend not a static page, so I still dont find a lib to compile the C backend to WASM. The in-browser compiler is still experimental anyway.

What you can try in the demo

The dropdown has a few starter programs — a DOM counter, a Canvas2D animation, and mouse/keyboard input — all compiled to WASM in your browser and running live, no server round-trip.

u/JiaHajime — 2 months ago
▲ 64 r/nim

Nim 2.2.4 compiler running entirely in the browser — Nim → C → WebAssembly

I managed to get Nim 2.2.4 compiling and running entirely client-side. There’s no backend, no native toolchain, and no JavaScript fallback. You paste your Nim code, hit Build & Run, and it compiles to actual WebAssembly right in your browser and executes it.

🔗 Live demo: https://benagastov.github.io/Nim-WASM-Compiler/

💻 Source: https://github.com/benagastov/Nim-WASM-Compiler

The Pipeline (100% In-Browser)

  • .nim code → Nim 2.2.4 (itself compiled to wasm) emits C.
  • clang.wasm → Compiles each .c file to an object file (.o).
  • lld.wasm → Links those objects into a single .wasm binary.
  • WebAssembly.instantiate → Executes the binary.

It relies on actual clang and lld binaries ported to WebAssembly doing the heavy lifting—not a transpiler or a hosted API.

The Bug That Ate My Week

The pipeline kept randomly dying halfway through compiling with RuntimeError: unreachable. I wasted time chasing two entirely wrong theories (dlmalloc heap exhaustion, then an lld assertion).

The real culprit was a bug in LLVM 8.0.1's WebAssembly object writer. It traps when serializing a common-linkage global. Because Clang 8 defaults to -fcommon, and Nim's generated C has several tentative definitions (threadId, allocator, roots...), the object writer just choked.

I delta-debugged the IR down to a single line that reproduces the trap:

Code snippet

u/threadId__system_u2938 = common hidden global i32 0

The fix: Three simple source-level changes. Adding -fno-common, a weak raise() stub, and rewriting Nim's 3-arg main to the 2-arg form that wasi's crt1.o expects. Zero binary patches to clang/lld were needed.

Default Example Output:

Plaintext

Hello, browser!
sorted: @[1, 1, 2, 3, 4, 5, 6, 9]
5! = 120

Credits to binji/clang.js for the wasm-compiled clang/lld toolchain that made this possible.

reddit.com
u/JiaHajime — 2 months ago