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-threadedThe :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 lists —
linux/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.
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=initOpenCascade3. Run the image
docker run --rm \
-v "$(pwd):/src" \
-u "$(id -u):$(id -g)" \
ghcr.io/taucad/opencascade.js:single-threaded \
link mybuild.ymllink 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 binarymy-occt.js— the emscripten loadermy-occt.d.ts— generated TypeScript declarationsmy-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.comA 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):
| Step | Warm cache | Cold cache |
|---|---|---|
| Image pull (compressed ~1.3 GB) | 0 s | 30-60 s |
generate against your YAML | 5-15 s | 5-15 s |
compile-bindings (cached .o files) | 0 s | 1-3 min |
compile-sources (cached OCCT .a) | 0 s | 5-10 min |
| Link against precompiled OCCT objects | 30-90 s | 30-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.