| Field | Value |
|---|---|
| Status | Stable |
| Last updated | Sunday, 22 June 2026 |
π This README is also available in other languages: EspaΓ±ol | PortuguΓͺs
Interactive educational platform about Web Workers built with Angular 22. It ships 16 progressive examples with live demos and real thread visualization, multi-language support (ES/EN/PT), and 5 swappable visual themes β the same neutral domain rendered five different ways.
βΆ Live demo: damiansire.github.io/web-worker-patterns
On any system (Windows, macOS, Linux):
git clone https://github.com/damiansire/web-worker-patterns.git
cd web-worker-patterns
npm run devnpm run dev checks Node.js (β₯18) and npm (β₯9), installs dependencies if needed, and starts the server at http://localhost:4200.
Alternatives:
- Just start (if you already ran
npm install):npm start - Windows: double-click
scripts/start/start.bat, or in a terminal:npm run dev - macOS/Linux: in a terminal:
./scripts/start/start.shornpm run dev
Open http://localhost:4200 in your browser.
The 16 examples are organized into 5 categories by concept. The grouping below mirrors src/app/core/domain/examples/examples.registry.ts β the single source of truth.
| # | Example | Description |
|---|---|---|
| 01 | Counter with setInterval | How setInterval and the event loop work β the baseline to grasp before workers. |
| 02 | The main thread & event loop | One thread runs JS, layout, paint and input; a 50 ms task freezes everything. |
| 16 | Compositor vs main | The compositor thread keeps transform/opacity animations smooth even while the main thread is frozen. |
| # | Example | Description |
|---|---|---|
| 03 | Basic communication | The "Hello World" of workers β postMessage in both directions. |
| 08 | SharedWorker | One worker instance shared across tabs/panels, all seeing the same state. |
| # | Example | Description |
|---|---|---|
| 04 | Offload heavy work | Count primes on a worker so the UI stays responsive β feel the difference side by side. |
| 07 | Transferable objects | Pass an ArrayBuffer zero-copy; the sender's buffer is left detached. |
| 10 | Worker pool | A fixed pool of N workers drains a task queue (4 workers, 24 tasks). |
| 14 | OffscreenCanvas | A worker owns the canvas and animates it β smooth even when the main thread blocks. |
| 15 | The cost of cloning | Measure the real round-trip of structured clone as data size and complexity grow. |
| # | Example | Description |
|---|---|---|
| 05 | Error handling | A worker error doesn't crash the page; the main thread catches it. |
| 06 | Lifecycle & termination | Create, run and terminate() β the in-flight step is lost and the worker can't be reused. |
| 09 | Limits of parallelism | navigator.hardwareConcurrency caps real parallelism; beyond it, workers share cores. |
| # | Example | Description |
|---|---|---|
| 11 | Backpressure | Flow control with credits and acks so the worker's queue doesn't grow without bound. |
| 12 | SharedArrayBuffer | Main and worker share the same memory; Atomics write/read with no postMessage. |
| 13 | Graceful degradation | Detect typeof Worker and fall back to the main thread when it's missing. |
The same domain is skinned by 5 independent themes, switchable live from the UI:
Brutalist Β· Full Brutalist Β· Dev Tool Β· Editorial Β· Narrative
Each theme provides its own shell, home, example layout, UI primitives and a custom thread visualizer β without the domain ever knowing a theme exists.
The golden rule: the domain is written once; the presentation is written five times. core/ is theme-neutral and never imports from themes/. The full rationale lives in ARQUITECTURA-multi-theme.md.
src/app/
βββ core/ # Neutral domain β knows nothing about themes
β βββ domain/
β β βββ workers/ # Real Web Workers + pure *.logic.ts (unit-tested)
β β βββ examples/ # examples.registry.ts (source of truth) + code snippets
β βββ services/ # Per-example demo services (signals-based)
β βββ i18n/ # Transloco loader
β βββ styles/ # Shared SCSS (_buttons, _containers, _example-layout)
β βββ utils/ # Helpers (code-snippet.helper)
βββ theming/ # Theme registry, service, guard, token contract
βββ ui-contracts/ # Interfaces every theme primitive must satisfy
βββ ui-primitives/ # Theme-agnostic primitives (charts, language switcher)
βββ themes/ # Presentation β one folder per theme
β βββ brutalist/ full-brutalist/ dev-tool/ editorial/ narrative/
β β βββ shell/ home/ example-layout/ primitives/ styles/
βββ app.routes.ts # Routes generated from the examples registry
βββ app.ts # Root component
public/i18n/ # en.json Β· es.json Β· pt.json (UI + per-example content)
A new example is one neutral domain definition rendered by all 5 themes. The full pipeline is codified in the /migrate-example command (.claude/commands/migrate-example.md). In short:
- Add the worker in
core/domain/workers/(+ a pure*.logic.tswith a unit test) and its snippets undercore/domain/examples/snippets/. - Register it in
core/domain/examples/examples.registry.ts(id,order,category,demo,workerFactory). - Add the educational content (title, summary, takeaways) to
public/i18n/{en,es,pt}.json. - Wire the demo's visualization into the
@caseof each theme's example layout.
Routes, navigation and the home page update automatically from the registry.
- Angular 22 β Standalone components, Signals, zoneless change detection (opt-in via
provideZonelessChangeDetection()), esbuild-based build - TypeScript 6.0.3
- SCSS β Semantic design tokens (
--surface,--ink,--accent,--thread-*) per theme - @jsverse/transloco β Runtime i18n (ES/EN/PT)
- highlight.js β Syntax highlighting for code blocks
- Vitest β Unit tests (100+ tests across the pure domain logic, the services and the themes)
- dependency-cruiser β Enforces the
core/ β themes/boundary - Web Workers API β Dedicated Workers, SharedWorker, Transferable Objects, SharedArrayBuffer + Atomics, OffscreenCanvas
This project uses Angular's built-in worker support (@angular/build / esbuild). Other setups use worker-plugin (webpack), rollup-plugin-off-main-thread, or Parcel's native worker support.
This project targets Angular 22 with @angular/build and a dedicated webWorkerTsConfig for worker bundles.
If you are upgrading from an older major version, use the Angular update tool and follow the official migration guidance:
ng update @angular/core @angular/cliThen:
- Zoneless (opt-in): Angular is not zoneless by default; this app enables it with
provideZonelessChangeDetection()inapp.config.ts(which is why it ships no zone.js in polyfills). It relies on Signals andcomputed()for change detection. - Tests:
tsconfig.spec.jsonexcludes**/*.worker.ts, so worker files are not compiled with the DOMWindowtype. - Build:
@angular/buildis the standard builder and bundles the app with esbuild.
npm run dev # Checks Node/npm, installs deps if needed and starts the server (all platforms)
npm start # Dev server at localhost:4200 (requires a prior npm install)
npm run build # Production build
npm test # Gate: ESLint + ng build + unit tests (Vitest)
npm run lint # ESLint: no-console, mandatory OnPush, keyboard a11y
npm run format # Format the code with Prettier
npm run format:check # Check formatting without writing (CI gate)
npm run lint:boundaries# Enforce the golden rule (core/ β themes/)Quality gates (lint, build, format, tests, boundaries) run on every push/PR via CI and as a local git pre-commit hook β independent of your editor. ESLint enforces the invariants the repo preaches: no-console in the lib, ChangeDetectionStrategy.OnPush on every component (the app is zoneless), and keyboard a11y in templates. See AGENTS.md and docs/AI-PROCESS.md.
The application supports English, Spanish, and Portuguese via Transloco. Translations live in:
public/i18n/en.jsonΒ·public/i18n/es.jsonΒ·public/i18n/pt.jsonβ UI text and the educational content for every example
Switch languages from the selector in each theme's shell.
- Docker Guide β Run the project with Docker
- Docker Guide (ES) | Docker Guide (PT)
- Multi-theme architecture β The design source of truth
- AI process β Gates, design-review loop, tooling
- Use web workers to run JavaScript off the browser's main thread (web.dev) β Why off-main-thread architecture helps Core Web Vitals (INP, LCP) and reduces main-thread contention.
- Comlink β Use workers without writing
postMessageby hand; expose an API that returns promises. This repo uses the native API to teach the fundamentals. - PROXX β Case study: Minesweeper clone with game logic in a worker and rendering on the main thread; see the web.dev article for the tradeoffs (reducing risk and improving UX rather than raw speed).
MIT