Mercurial
comparison mrjunejune/src/blog/wasm-bunny/index.md @ 100:65e5a5b89a4e
[Seobeo] Migrated everything to this page.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sat, 03 Jan 2026 07:48:07 -0800 |
| parents | |
| children | 295ac2e5ec00 |
comparison
equal
deleted
inserted
replaced
| 99:684edfaf93b7 | 100:65e5a5b89a4e |
|---|---|
| 1 # First Steps with C3 and Raylib on WebAssembly | |
| 2 | |
| 3 Hi! This is my first blog post on this website, and hopefully, it’s the first of many, as I have some free time at the moment. | |
| 4 | |
| 5 Today, I spent a few hours experimenting with the programming language [C3](https://c3-lang.org/) and linking it with a static file from [Raylib](https://www.raylib.com/), a popular library for creating web applications. My goal was to build something interactive and deploy it to the web with minimal hassle. | |
| 6 | |
| 7 I’ve set up a repository to link C3 with Raylib and stress-test it using a bunny-rendering benchmark, inspired by this [bunnymark benchmark](https://old.reddit.com/r/raylib/comments/15jy1x3/raylib_bunnymark_benchmark_with_100k_bunnies/). | |
| 8 | |
| 9 Here are some key things to keep in mind when compiling C3 and Raylib to WebAssembly (WASM): | |
| 10 | |
| 11 ## Limited WASM Support in C3: | |
| 12 | |
| 13 C3 has limited support for compiling to [WASM](https://c3-lang.org/faq/#platform-support). The standard library isn't usable in WASM, so any standard functions need to be rewritten or excluded. For example, I attempted to randomly assign colors to a rabbit, but there was no compile-time error, so I had to debug why WASM wasn’t loading properly. In hindsight, I should have known, given that the `--link-libc=no` flag was used in the examples. | |
| 14 | |
| 15 Example command: | |
| 16 ``` | |
| 17 c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry main.c3 raylib.c3 | |
| 18 ``` | |
| 19 | |
| 20 ## Custom Raylib JavaScript File: | |
| 21 | |
| 22 Raylib APIs need to be called through a custom `raylib.js` file. As shown in the command above, there’s no static Raylib file (`raylib.a`) linked directly to WASM. Instead, Raylib APIs are accessed through JavaScript using [WebAssembly.instantiateStreaming](https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming_static). For example, `raylib::init_window` would be called in JavaScript like this: | |
| 23 | |
| 24 ```javascript | |
| 25 function InitWindow(width, height, title_ptr) { | |
| 26 this.ctx.canvas.width = width; | |
| 27 this.ctx.canvas.height = height; | |
| 28 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 29 document.title = cstr_by_ptr(buffer, title_ptr); | |
| 30 } | |
| 31 | |
| 32 const raylibObject = { | |
| 33 raylib: { InitWindow: InitWindow }, | |
| 34 }; | |
| 35 | |
| 36 WebAssembly.instantiateStreaming(fetch("main.wasm"), raylibObject).then( | |
| 37 (obj) => obj.instance.exports.exported_func(), | |
| 38 ); | |
| 39 ``` | |
| 40 | |
| 41 To actaully link it together, I was lucky to find raylib.js [this repo](https://github.com/tsoding/c3-demo/blob/main/raylib.js) (Thanks tsoding!!). I only needed to add a few functions for handling clicks. The file had `RaylibJs` class which need to be turned into object from above example and used `Proxy` class to do that. I actaully never seen that API being used outside of this. | |
| 42 | |
| 43 ```javascript | |
| 44 function make_environment(env) { | |
| 45 return new Proxy(env, { | |
| 46 get(_target, prop, _receiver) { | |
| 47 if (env[prop] !== undefined) { | |
| 48 return env[prop].bind(env); | |
| 49 } | |
| 50 return (...args) => { | |
| 51 throw new Error(`NOT IMPLEMENTED: ${prop} ${args}`); | |
| 52 }; | |
| 53 }, | |
| 54 }); | |
| 55 } | |
| 56 | |
| 57 class RaylibJs { | |
| 58 // will have all raylib functions | |
| 59 ... | |
| 60 | |
| 61 IsMouseButtonPressed(key) { | |
| 62 return this.currentIsMouseButtonPressed == key; | |
| 63 } | |
| 64 | |
| 65 // entrypoints to fetch wasm and start it. | |
| 66 start(wasmPath, canvasId) { | |
| 67 ... | |
| 68 | |
| 69 this.wasm = await WebAssembly.instantiateStreaming(fetch(wasmPath), { | |
| 70 env: make_environment(this), | |
| 71 }); | |
| 72 | |
| 73 // Call the main functions from wasm object | |
| 74 this.wasm.instance.exports.main(); | |
| 75 } | |
| 76 } | |
| 77 ``` | |
| 78 | |
| 79 Overall, this was an interesting project to spend a few hours on, and maybe in the future, I’ll explore compiling the C3 standard library to WASM. | |
| 80 | |
| 81 Below are the results! |