39 releases (7 breaking)

0.8.0 Jun 7, 2020
0.6.9 Mar 16, 2020
0.6.8 Dec 27, 2019
0.6.4 Nov 22, 2019

#274 in WebAssembly

Download history 48/week @ 2022-12-06 93/week @ 2022-12-13 56/week @ 2022-12-20 22/week @ 2022-12-27 24/week @ 2023-01-03 22/week @ 2023-01-10 16/week @ 2023-01-17 43/week @ 2023-01-24 47/week @ 2023-01-31 84/week @ 2023-02-07 143/week @ 2023-02-14 76/week @ 2023-02-21 54/week @ 2023-02-28 24/week @ 2023-03-07 18/week @ 2023-03-14 139/week @ 2023-03-21

235 downloads per month
Used in 4 crates


2.5K SLoC

Rust 2K SLoC // 0.0% comments JavaScript 670 SLoC // 0.0% comments C 28 SLoC


this library creates a bridge to javascript in the browser at runtime using web assembly

docs.rs docs

A foreign function interface(FFI) library for invoking Javascript functions from Web Assembly for many programming languages

  • no code generation or special cargo components
  • support for callbacks (e.g. setTimeout)
  • futures based on callbacks
  • memory as a parameter
  • wrapper library for Rust
  • works with C or C++, check out examples here
  • typed arrays
  • can be executed in a web worker

This project has similarities to Javascript's <function>.call(<object>,a0,a1,...) but with the limitations of Web Assembly's function call restrictions.

Hello World! in Rust

note js_ffi is language agnostic, I just used Rust as example because I like it

js_ffi = "0.6"
use js_ffi::*;#[no_mangle]
pub fn main() -> () {
    register_(console.log).invoke_1("Hello World");
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi@latest/js_ffi.js"></script>
# cli commands for building web assembly
	@RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
	@cp target/wasm32-unknown-unknown/release/helloworld.wasm .
	@cargo fmt
	python3 -m http.server 8080


See demo here

use js_ffi::*;

fn main() {
    let screen = register_function("document.querySelector").call_1(&DOCUMENT, "#screen").to_js_object();
    let ctx = register_function("document.querySelector").call_1(&screen, "#screen").to_js_object();

    let fill_style = register_function("function(color){
        this.fillStyle = color;
    let fill_rect = register_function("CanvasRenderingContext2D.prototype.fillRect");

    fill_style.call_1(&ctx, "red");
    fill_rect.call_4(&ctx, 0.0, 0.0, 50.0, 50.0);

    fill_style.call_1(&ctx, "green");
    fill_rect.call_4(&ctx, 15.0, 15.0, 50.0, 50.0);

    fill_style.call_1(&ctx, "blue");
    fill_rect.call_4(&ctx, 30.0, 30.0, 50.0, 50.0);

Event Listener

use js_ffi::*;

fn main() {
    let btn = register_function("document.querySelector").call_1(&DOCUMENT, "#button").to_js_object();
        create_callback_0(|| {
            register_function("window.alert").invoke_1("I was clicked");

Async Example

Using an executor library we can easily turn callbacks into futures and run behavior asynchronously.

use js_ffi::*;

pub fn main() -> () {
    executor::spawn(async {
        let console_log = register_function("console.log");

fn sleep(millis: u32) -> impl core::future::Future {
    let set_timeout = register_function("window.setTimeout");
    let (future, cb) = create_callback_future_0();
    set_timeout.invoke_2(cb, millis);

Third Party

Wrap third party libraries. Anything function in global space should be able to be wrapped and invoked.

use js_ffi::*;

fn main() {
    let jquery_handle = register_function("$");
    let jquery_on_handle = register_function("jQuery.prototype.on");
    let alert = register_function("(msg)=>window.alert(msg)");

    let body = jquery_handle.invoke_1("body").to_js_object();
        create_callback_1(move |_event| {
            alert.invoke_1("I was clicked!");
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script src="https://cdn.jsdelivr.net/gh/richardanaya/js_ffi/js_ffi.js"></script>

Standard Web Libraries

A collection of libraries exist that expose javascript functionality so you don't have to implement it yourself. Just add them to your project and go!

How it works

  1. Get a handle to some Javascript function using register_function. Re-use this handle as often as possible.
  2. If you are invoking this function as a regular function, use the appropriate invoke_* function based on the number of arguments you are passing (invoke_1,invoke_7,etc.).
  3. If you are invoking this function as a method of an object represented by a JSValue, use the appropriate call_* function based on the number of arguments you are passing (call_1,invoke_7,etc.) and make sure your object is the first paramter.

Don't like Rust?

The script js_ffi.js has nothing Rust specific.

  • Operations execute through an interface specified in this js_ffi.h
  • js_ffi expects an entry point main()
  • If you plan on having your module receive data it must implement jsffimalloc(i32) -> i32
  • If you plan on having your module receive callbacks it must implement jsfficallback(i32,f32,f32,f32,f32,f32,f32,f32,f32,f32,f32)
  • strings are simply c-strings in memory that end in a 0 character.

Run as a webworker

// main.js
let w = new Worker("worker.js");
// worker.js
onmessage = function() {


This project is licensed under either of

at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in js_ffi by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.