demo · v141

JS Prototype Bridge

Before custom descriptors, calling a method on a Wasm GC object from JavaScript meant importing the function explicitly and passing the object as the first argument. Custom descriptors let the Wasm module attach a real JavaScript prototype to its types — wasmObj.method(arg) works natively.

Heads up Requires Chrome 141+ behind the WebAssembly Custom Descriptors flag (chrome://flags/#enable-experimental-webassembly-features). The probe below feature-detects; the comparison snippets work regardless.
checking support…

before — bare wasm reference

const { instance } = await WebAssembly.instantiate(wasmBytes, {
  env: {},
});
const player = instance.exports.makePlayer("Lara", 100);
// no method calls — only exported functions
const damaged = instance.exports.takeDamage(player, 20);
const score = instance.exports.getScore(damaged);
console.log("score:", score);

after — custom descriptor + JS prototype

// wasm module declares Player has a JS prototype with `takeDamage` + `score`
const { instance } = await WebAssembly.instantiate(wasmBytes);
const player = instance.exports.makePlayer("Lara", 100);
// methods on the wasm object — same syntax as a JS class
player.takeDamage(20);
console.log("score:", player.score);
click probe to run the support check

the wasm side (simplified)

;; module declares the Player type with a JS prototype
(type $Player (struct (field $name (mut stringref))
                       (field $hp   (mut i32))))

(custom-descriptor $Player
  (js-prototype "Player"
    (methods
      (method "takeDamage" (param $self (ref $Player)) (param $dmg i32) ...)
      (getter "score"      (param $self (ref $Player)) ...))))

why this angle

Wasm GC (shipped in 119) gave Wasm modules first-class objects with fields. But the JS interop story remained awkward: every method had to be exported as a separate function, called as module.method(obj, args). Custom descriptors close the loop — Wasm modules can present themselves to JS as if they were ergonomic JS classes. For library authors (Wasm-compiled image codecs, AI runtimes, game engines), this is the difference between "you have to write a JS wrapper" and "your wasm just works."

see also