Skip to main content

OCCT Worker Protocol Specification

The Bitbybit OCCT Kernel in Bitbybit Runners is loaded in a separate Web Worker at runtime. It is not compiled into the main runner bundle, so the runner can talk to a worker of your own that speaks the same protocol, hosted with the rest of the assets and reached through cdnUrl.

This document specifies the complete message protocol between the runner (main thread) and the OCCT worker, for building or debugging a compatible worker.

Using a modified OCCT

To run the kernel with a modified OpenCASCADE, you relink the kernel, then use a worker that works with the glue your link produced (the Runner's own worker modified for your own use, or one built around your glue), since the Runner's worker bundles the glue of our kernel: the Runner Licensing page says how to get the materials and how the Runner then loads your kernel.

Architecture Overview​

Main Thread (Runner) OCCT Web Worker
┌─────────────────────┐ ┌─────────────────────────┐
│ │ postMessage │ │
│ Runner init │ ──────────────►│ Load WASM │
│ │ {type: │ Load fonts │
│ │ "initialise"}│ Init the CAD kernel │
│ │ │ │
│ │◄────────────── │ postMessage │
│ State: initialised │ "occ- │ ("occ-initialised") │
│ │ initialised" │ │
│ │ │ │
│ API call │ ──────────────►│ Execute function │
│ {action, uid} │ │ Return result │
│ │◄────────────── │ {uid, result} │
│ │ │ │
└─────────────────────┘ └─────────────────────────┘

The worker is spawned as an ES module worker:

const workerUrl = URL.createObjectURL(
new Blob([`import "${cdnUrl}/workers/bitbybit-dev-occt-webworker.js";`],
{ type: "text/javascript" })
);
const worker = new Worker(workerUrl, { type: "module", name: "OCC_WORKER" });

Message Protocol​

1. Initialization​

Main thread → Worker:

worker.postMessage({
type: "initialise",
loadFonts: ["Roboto", "Tektur"] // array of font family names, or []
});
FieldTypeDescription
typestringMust be exactly "initialise" (British spelling)
loadFontsstring[]Font families to load for CAD text operations. Pass [] to skip font loading.

Worker → Main thread (on success):

postMessage("occ-initialised")

The worker must post the exact string "occ-initialised" when the WASM kernel is loaded, initialized, and ready to accept API calls. The runner will not send any API calls until this message is received.

2. API Calls​

Main thread → Worker:

worker.postMessage({
action: {
functionName: "shapes.wire.createCircleWire",
inputs: {
radius: 5,
center: [0, 0, 0],
direction: [0, 1, 0]
}
},
uid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
});
FieldTypeDescription
action.functionNamestringDot-path identifying the OCCT function (e.g., "shapes.wire.createCircleWire", "operations.extrude", "io.saveShapeSTEP")
action.inputsobjectKey-value pairs matching the function's parameters. Shape references use the format described below.
uidstringUnique identifier for correlating request/response. Any unique string (e.g., a UUID).

3. Responses​

For every API call, the worker posts two messages:

Step 1 - Busy notification:

postMessage("busy")

The exact string "busy" signals that computation has started.

Step 2a - Success:

postMessage({
uid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890", // echoed from request
result: { type: "occ-shape", hash: "abc123" } // or any serializable value
})

Step 2b - Error:

postMessage({
uid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
result: undefined,
error: "OCCT computation failed. <Error message> While executing function - shapes.wire.createCircleWire. Input values were: {radius: 5}. "
})
FieldTypeDescription
uidstringMust match the request uid
resultanyThe function's return value. undefined on error.
errorstringError description. Present only on failure.

4. Shape References​

Shapes (TopoDS_Shape objects) cannot be sent across the worker boundary directly. They are represented as references:

// Shape reference (TopoDS_Shape and subclasses)
{ type: "occ-shape", hash: "abc123def" }

// Entity reference (documents, assembly nodes, non-shape OCCT objects)
{ type: "occ-entity", hash: "xyz789" }
  • When the main thread sends a shape reference in inputs, the worker must resolve it to the actual cached shape object.
  • When the worker returns a shape, it must return a reference (not the raw object).
  • The hash is an opaque identifier managed by the worker's internal cache.

5. Reserved Functions​

These function names have special behavior and bypass the standard function dispatch:

Function NamePurpose
shapeToMeshConvert a shape to mesh data for rendering
shapesToMeshesConvert multiple shapes to mesh data
docToMeshConvert an assembly document to mesh data
docToMeshesConvert several assembly documents to mesh data
deleteShapeRemove a single shape from the internal cache
deleteShapesRemove multiple shapes from the internal cache
deleteDocumentRemove an assembly document handle from the cache
startedTheRunSignal a new computation run; may clean cache if too large
cleanAllCacheClear all cached shapes and entities, and any meshes kept on them
setMeshRetentionSet how many triangles meshing may keep on shapes for reuse ({ triangles }, 0 keeps none); answers with what is kept
addOcAdd OpenCascade dependencies or plugins
saveShapeSTEPExport a shape to STEP format

6. Worker State Lifecycle​

loading → initialised → loaded ↔ computing
StateMeaning
loadingWorker is spawned but WASM not yet initialized
initialisedWASM loaded, fonts loaded, ready for API calls
computingProcessing an API call (triggered by "busy" message)
loadedIdle, no pending calls in the queue

Replacing the OCCT Worker​

The simplest way to run your own worker (and a relinked kernel) is to host the worker and WASM files yourself, maintaining the expected directory structure:

https://your-cdn.com/bitbybit/
├── workers/
│ └── bitbybit-dev-occt-webworker.js ← your custom worker
└── wasm/
└── bitbybit-dev-occt.*.wasm ← your custom WASM binary

Then initialize the runner with your CDN URL:

const runner = window.bitbybitRunner.getRunnerInstance();
await runner.run({
canvasId: 'myCanvas',
enableOCCT: true,
cdnUrl: 'https://your-cdn.com/bitbybit/'
});
File Names Matter

The runner constructs worker URLs by appending fixed file names to the cdnUrl. Your replacement files must use the same names as the originals (e.g., bitbybit-dev-occt-webworker.js).

WASM Resolution​

Inside the worker, the WASM binary path is resolved via Emscripten's locateFile hook:

locateFile(path, scriptDirectory) {
if (path.endsWith(".wasm")) {
return cdnUrl + "/wasm/bitbybit-dev-occt.<hash>.wasm";
}
return scriptDirectory + path;
}

If you relink the kernel (Runner Licensing), update locateFile in your worker to point to your binary (or keep the original file name).

Building a Compatible Worker​

Your custom worker must:

  1. Listen for "initialise" messages and load your WASM kernel
  2. Post "occ-initialised" when ready
  3. Listen for API call messages with { action: { functionName, inputs }, uid } format
  4. Post "busy" before each computation
  5. Post { uid, result } or { uid, result: undefined, error } responses for each call
  6. Handle shape references ({ type: "occ-shape", hash }) in inputs and outputs
  7. Implement the reserved functions listed above

Worker Variants​

Three OCCT worker variants exist, selectable via the occtArch option:

OptionWorker FileWASMNotes
"32" (default)bitbybit-dev-occt-webworker.jsbitbybit-dev-occt.*.wasmSingle-threaded, 32-bit
"64"bitbybit-dev-occt-64-bit-webworker.jsbitbybit-dev-occt-64-bit.*.wasmSingle-threaded, 64-bit
"64-mt"bitbybit-dev-occt-64-bit-mt-webworker.jsbitbybit-dev-occt-64-bit-mt.*.wasmMulti-threaded (pthreads), 64-bit

The multi-threaded variant additionally spawns pthread sub-workers. If replacing the 64-bit MT worker, you must also handle mainScriptUrlOrBlob for cross-origin pthread loading.