demo · v141

WASM Type Explorer

Parse WAT-like type definitions, visualise the memory layout of GC structs, and see how custom descriptors let you attach a JS prototype to each type — turning opaque WASM references into first-class JS objects your devtools can inspect.

Experimental flag Full Custom Descriptor support requires chrome://flags/#enable-experimental-webassembly-features. The type parser, layout visualiser, and prototype-bridge demo below work without the flag — only the live WebAssembly.Module.customDescriptors() call requires it.
probing WebAssembly GC + Custom Descriptors…

WAT type DSL — edit and click Parse

type tree

Click Parse to inspect types.

memory layout

Click Parse to see field offsets.

before vs after custom descriptors

pre-141 / no descriptor

WASM object is opaque to JS

// JS side
const pt = module.makePoint(3, 4);
// pt is an opaque externref —
// no prototype, no property access

// Must call module functions
const d = module.distance(pt);
// Cannot inspect pt in DevTools
Chrome 141 + descriptors

WASM object has JS prototype

// JS side — custom descriptor
// attached $pointDesc prototype
const pt = module.makePoint(3, 4);
// pt now has JS prototype with methods

pt.distance(); // calls WASM func
pt.x; pt.y;   // field accessors
// DevTools shows: Point { x:3, y:4 }

use case: image processing library

Without custom descriptors, a WASM pixel-manipulation library must route all operations through module-level functions. With descriptors, each Pixel struct gains a prototype with methods — pixel.toHex(), pixel.luminance() — callable directly from JS. DevTools can inspect the struct's fields. JSON serialisation works via the prototype's toJSON(). The WASM ↔ JS boundary disappears from the consumer's perspective.

the WAT descriptor syntax

;; Custom Descriptors proposal (WAT notation — illustrative)
(module
  ;; Define descriptor type that carries the JS prototype
  (type $pointDesc (descriptor
    (struct (field $proto externref))
  ))

  ;; Struct type with an associated custom descriptor
  (type $Point (descriptor $pointDesc
    (struct
      (field $x f64)
      (field $y f64)
    )
  ))

  ;; Factory: creates a Point struct with host prototype wired in
  (func (export "makePoint") (param $x f64) (param $y f64) (result (ref $Point))
    (struct.new $Point (local.get $x) (local.get $y)))

  ;; Method: accessible as pt.distance() via prototype chain
  (func (export "distance") (param $p (ref $Point)) (result f64)
    (f64.sqrt (f64.add
      (f64.mul (struct.get $Point $x (local.get $p)) (struct.get $Point $x (local.get $p)))
      (f64.mul (struct.get $Point $y (local.get $p)) (struct.get $Point $y (local.get $p))))))
)

see also