OpenCascade.js

Quickstart — Docker

Pull the prebuilt GHCR image, link a custom YAML, and build a trimmed OCCT wasm in under 5 minutes.

For when you need a custom-trimmed opencascade.wasm without setting up emsdk, libclang, and Python locally.

1. Pull the image

docker pull ghcr.io/taucad/opencascade.js:single-threaded

The :single-threaded tag carries pre-compiled OCCT static libraries so your build only pays for the link step. For threaded builds (requires COOP/COEP on consumer pages), pull :multi-threaded instead. For custom-bindings work that needs the pre-PCH/generate state but not the pre-compiled libraries, see :bindgen-base in Reference → Docker image.

Supported platforms

Release tags (:single-threaded, :multi-threaded, :bindgen-base, versioned variants) ship as multi-architecture manifest listslinux/amd64 and linux/arm64 — so Apple Silicon and ARM Linux hosts pull the native arch automatically. No --platform flag required.

Branch tags (:branch-<slug>, :multi-threaded-branch-<slug>, :bindgen-base-branch-<slug>) are linux/amd64 only by design — branch publishes need to be fast for reviewers, and the per-arch image build cost is the dominant factor. ARM hosts pulling a branch tag will run under Rosetta / QEMU. Use a release tag whenever native performance matters.

2. Write a build YAML

Create mybuild.yml next to your project — list the OCCT symbols you actually use. See Trim symbols for a worked example and YAML schema for the full schema.

mybuild.yml
mainBuild:
  name: my-occt
  bindings:
    - symbol: package
      glob: 'gp'
    - symbol: package
      glob: 'BRepPrim*'
    - symbol: package
      glob: 'BRepAlgoAPI'
    - symbol: package
      glob: 'BRepBuilderAPI'
    - symbol: package
      glob: 'BRepFilletAPI'
    - symbol: package
      glob: 'TopoDS'
    - symbol: package
      glob: 'TopExp'
    - symbol: package
      glob: 'STEPControl'
    - symbol: package
      glob: 'RWGltf*'
  emccFlags:
    - -O3
    - -fwasm-exceptions
    - -sMODULARIZE=1
    - -sEXPORT_ES6=1
    - -sEXPORT_NAME=initOpenCascade

3. Run the image

docker run --rm \
  -v "$(pwd):/src" \
  -u "$(id -u):$(id -g)" \
  ghcr.io/taucad/opencascade.js:single-threaded \
  link mybuild.yml

link is the end-to-end command. Inside the container Nx's dependsOn graph walks every upstream step (apply-patches → pch → generate → compile-bindings → compile-sources → link) with cache reuse, so a fresh container performs a full build and cached re-runs replay only the link step.

Output lands next to mybuild.yml:

  • my-occt.wasm — the trimmed wasm binary
  • my-occt.js — the emscripten loader
  • my-occt.d.ts — generated TypeScript declarations
  • my-occt.build-manifest.json — symbol coverage and size report

4. Wire the build output

import init from './my-occt.js';
import wasmUrl from './my-occt.wasm?url';

const oc = await init({ locateFile: () => wasmUrl });

5. Pin by manifest-list digest in production

Tags can be re-pointed; digests cannot. Resolve the manifest-list digest once and pin it in CI so every reproducer of your build pulls the exact same multi-arch image set:

docker buildx imagetools inspect ghcr.io/taucad/opencascade.js:single-threaded \
  --format '{{json .Manifest.Digest}}'
# → "sha256:abc123…"

# Pin in CI / docker-compose / Dockerfile:
ghcr.io/taucad/opencascade.js@sha256:abc123…

The manifest-list digest covers both linux/amd64 and linux/arm64 sub-images — pulling by digest from any arch resolves to the same deterministic build.

See Reproducible CI for the full workflow.

6. Verify the supply-chain signature (optional)

Every published image is signed with cosign via OIDC keyless signing — no rotating private keys, signatures published to the Sigstore Rekor transparency log. To verify before pulling:

cosign verify ghcr.io/taucad/opencascade.js:single-threaded \
  --certificate-identity-regexp 'https://github.com/taucad/opencascade\.js/\.github/workflows/docker\.yml@.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

A successful verification confirms the image was built by the taucad/opencascade.js GitHub Actions workflow and has not been tampered with since publication.

Where the time goes

Typical wall-clock for a 22-50 symbol trim on the GitHub Actions ubuntu-latest runner (the e2e gate budget; M1 Pro local times are 30-50% faster):

StepWarm cacheCold cache
Image pull (compressed ~1.3 GB)0 s30-60 s
generate against your YAML5-15 s5-15 s
compile-bindings (cached .o files)0 s1-3 min
compile-sources (cached OCCT .a)0 s5-10 min
Link against precompiled OCCT objects30-90 s30-90 s
Total~1-2 min~3-5 min

The CI e2e gate budgets a hard 5-minute warm-link ceiling and tags get published only if both :single-threaded and :multi-threaded clear that budget on both architectures. Cold-cache runs (first pull on a new machine) add the image pull time; subsequent runs on the same machine reuse the pulled image and Nx cache.

Full surface builds (no symbol filter, i.e. build-configs/full.yml) take 25-40 minutes — they're for the maintainer pipeline, not consumer machines.

On this page