Why Web Workers?
JavaScript is single-threaded by default. Heavy computation on the main thread causes:- UI freezing: No input events processed while computing
- Janky animations: Frame drops during geometry generation
- Poor UX: App feels unresponsive
- Running code in a separate OS thread
- Communicating via message passing (no shared memory)
- Keeping the main thread free for rendering and events
Worker Architecture
Gridfinity Builder uses a single persistent worker (manifoldWorker.ts) that:
- Initializes the Manifold WASM module once
- Receives bin configurations from the main thread
- Generates CSG meshes
- Transfers mesh buffers back to the main thread
Worker Implementation
The worker is defined inmanifoldWorker.ts:1-62:
manifoldWorker.ts:1-13
Message Handler
The worker listens for messages from the main thread (manifoldWorker.ts:33-61):
manifoldWorker.ts:33-61
Receive Request
Worker gets a message with
type (preview/export), config (bin parameters), and requestId (for response matching)Main Thread Interface
TheuseManifoldWorker.ts hook provides the main thread API for communicating with the worker.
Worker Singleton
A single worker instance is created and reused (useManifoldWorker.ts:12-32):
useManifoldWorker.ts:12-32
type: 'module' to support ES module imports (TypeScript, Manifold library).
Request-Response Matching
Since workers use asynchronous message passing, requests are tracked by unique IDs:useManifoldWorker.ts:8-10
pending map.
Promise-Based API
TherequestMesh function wraps the message-passing API in a Promise (useManifoldWorker.ts:41-60):
useManifoldWorker.ts:41-60
Cancellation Pattern
For interactive editing, only the latest request for a given bin should complete. TherequestBinMesh function cancels outdated requests (useManifoldWorker.ts:65-98):
useManifoldWorker.ts:78-80
Transferable Objects
Mesh buffers are transferred using Transferable Objects, avoiding expensive memory copies:manifoldWorker.ts:50-53
postMessage specifies which ArrayBuffers should be transferred (ownership moved) instead of copied. After transfer:
- The worker thread can no longer access these buffers
- The main thread owns the buffers
- No memory duplication occurs
- Without transfer: ~2.4 MB copied (100k × 3 floats × 4 bytes + 300k indices × 4 bytes)
- With transfer: ~0 bytes copied (ownership transferred)
Mesh Caching
TheuseManifoldWorker hook caches mesh results by configuration hash (useManifoldWorker.ts:38-39):
useManifoldWorker.ts:38-39
Cache Invalidation
The cache persists for the lifetime of the page. To clear it:useManifoldWorker.ts:100-102
Error Handling
If geometry generation fails, the worker catches the error and posts an error message (manifoldWorker.ts:54-59):
manifoldWorker.ts:54-59
Performance Impact
Without Web Workers
- Generating a 2×2×6 bin with dividers: ~200ms
- Main thread blocked for entire duration
- UI freezes, no input response
- Frame rate drops to 0 FPS
With Web Workers
- Same generation: ~200ms in background thread
- Main thread continues rendering at 60 FPS
- Input events processed immediately
- Smooth user experience
Browser Compatibility
Web Workers are supported in all modern browsers:- Chrome/Edge: Full support
- Firefox: Full support
- Safari: Full support (desktop and iOS)
type: 'module') require:
- Chrome 80+
- Firefox 114+
- Safari 15+
Limitations
- No DOM access: Workers cannot manipulate the DOM or call Three.js directly
- Message passing overhead: Small messages (<1KB) may be slower than direct calls
- WASM memory: Worker runs in a separate process with its own WASM heap (memory not shared with main thread)
