Web Workers are a powerful browser feature that let scripts run in the background, separate from the main thread that paints the page and handles clicks. That means you can crunch numbers, parse large files, or process data without freezing scroll, animation, or input.
This tutorial explains what workers are, how to create them, how messaging works, and how to avoid common mistakes—with five examples and three interactive Try It demos you can run in your browser.
What You’ll Learn
01
Worker()
Spawn a thread.
02
postMessage
Send data.
03
onmessage
Receive results.
04
onerror
Catch failures.
05
No DOM
Thread limits.
06
Try It
Live demos.
Fundamentals
What Are Web Workers?
Web Workers are a way to run JavaScript in background threads. Each worker has its own global scope (accessed as self) and runs independently of the main UI thread. You typically use them for:
Long mathematical or statistical calculations
Parsing large JSON or CSV files
Image, audio, or video processing
Sorting or filtering big arrays
Prefetching and transforming API data before display
Workers are not separate processes like OS threads in native apps—they are browser-managed threads with a strict security model. They are still one of the best tools for keeping pages responsive in plain JavaScript.
💡
Beginner Tip
Always check typeof Worker !== 'undefined' before calling new Worker(). Very old browsers and some restricted environments may not expose the API.
Architecture
How Do Web Workers Work?
Workers run in their own global context. They cannot access the DOM, window, document, or most browser APIs tied to the visible page. Communication happens through a messaging channel:
Main thread — creates the worker, sends jobs with worker.postMessage(), listens with worker.onmessage.
Worker thread — receives jobs in self.onmessage, does the work, replies with self.postMessage().
Structured clone — message data is copied between threads (objects, arrays, typed arrays). Functions and DOM nodes cannot be sent.
Because the worker is separate, a heavy loop inside it does not block the main thread—buttons stay clickable and animations keep running.
Setup
Creating a Web Worker
The classic pattern uses two files. On the main page, instantiate Worker with the URL of a JavaScript file:
js
const worker = new Worker('worker.js');
In worker.js, write the code that runs in the background thread:
js
self.onmessage = function (event) {
const result = performTask(event.data);
postMessage(result);
};
function performTask(data) {
// Perform computation or processing here
return data * 2; // Example: double the input
}
Inline worker (single-file demos)
In the Try It editor you often have only one HTML file. Put the worker code in a string and create a Blob—no <style> or extra files needed:
js
var code =
'self.onmessage = function(e) { postMessage(e.data * 2); };';
var worker = new Worker(
URL.createObjectURL(new Blob([code], { type: 'application/javascript' }))
);
Our Try It demos use this pattern so everything runs in one HTML page without extra server files.
Messaging
Communicating with Web Workers
Send data from the main thread with postMessage. Receive replies in onmessage:
js
// main.js
worker.postMessage(5); // Send data to the worker
worker.onmessage = function (event) {
console.log('Received from worker:', event.data);
};
Inside the worker, mirror the pattern—listen on self.onmessage, reply with postMessage:
Also call worker.terminate() when you no longer need the worker to free memory. Create a new Worker if you need to run jobs again later.
Watch Out
Common Pitfalls
DOM access — Workers cannot touch the DOM. Send results back to the main thread and update the page there.
Shared globals — Workers have their own global scope. Variables on window are not visible inside a worker, and vice versa.
Large message copies — postMessage clones data. Sending huge objects repeatedly can be slow; consider Transferable objects (like ArrayBuffer) for zero-copy moves.
Same-origin worker scripts — The worker script URL must obey the same-origin policy unless CORS allows it.
Over-using workers — Spawning a worker for tiny tasks adds overhead. Use workers when work genuinely risks blocking the UI.
Browser compatibility — Dedicated Workers are widely supported, but always feature-detect and provide a main-thread fallback for critical paths.
Cheat Sheet
⚡ Quick Reference
Task
Code pattern
Feature detect
if (typeof Worker !== 'undefined') { ... }
Create worker
const w = new Worker('worker.js')
Send to worker
w.postMessage(data)
Receive from worker
w.onmessage = (e) => e.data
Worker listens
self.onmessage = (e) => { ... }
Worker replies
postMessage(result)
Stop worker
w.terminate()
Create
new Worker(url)
Spawn thread
Send
postMessage()
Both sides
Listen
onmessage
Event handler
No DOM
self only
Worker scope
Hands-On
Examples Gallery
Five short examples from creating a worker to a full page. Each uses plain JavaScript—no styling required to understand the idea. Try It Yourself demos are minimal HTML with no CSS, using inline Blob workers so you do not need a separate worker.js file.
📚 Getting Started
Spawn a worker and exchange your first message.
Example 1 — Create a Web Worker
Check support, then create a worker. In real projects you pass a .js file path. In Try It demos we use an inline Blob (see Example 5).
js
if (typeof Worker === 'undefined') {
alert('Web Workers not supported');
} else {
var worker = new Worker('worker.js');
}
The same loop on the main thread would freeze animations and input. In a worker, the page keeps responding while the CPU crunches numbers in the background.
Example 5 — Example Usage (Full Page)
One simple HTML file. Click the button, send 10, worker returns 100. No CSS, no extra files—worker code lives inside the page as a string:
Games and editors — pathfinding, physics ticks, or syntax highlighting in the background.
Progressive enhancement — move expensive work off the main thread when Worker is available.
🧠 How Web Workers Work (Step by Step)
1
Feature detect
Confirm typeof Worker before creating a thread.
Guard
2
new Worker(url)
Browser loads the worker script on a background thread.
Spawn
3
postMessage(job)
Main thread sends input data (cloned) to the worker.
Send
4
Worker computes
Heavy logic runs without blocking UI events.
Process
=
⚡
onmessage → DOM
Main thread receives the result and updates the page.
Important
📝 Notes
Workers are not a replacement for server-side scaling—they help one browser tab stay responsive.
Service Workers (for offline caching) are a different API from Web Workers (for background computation).
Module workers: new Worker('worker.js', { type: 'module' }) lets worker scripts use import.
Terminate idle workers with worker.terminate() to avoid leaking threads.
For tiny tasks (< few ms), main-thread code is often faster than worker startup overhead.
Try It demos use Blob URLs so you can experiment without uploading separate .js files.
Compatibility
Universal Browser Support
Dedicated Web Workers are supported in all major modern browsers: Chrome, Firefox, Safari, and Edge. Shared Workers and Module Workers have slightly narrower support—check caniuse.com before relying on them in production.
✓ Baseline · Since HTML
Web Workers API
Dedicated Web Workers are supported in all major modern browsers: Chrome, Firefox, Safari, and Edge. Shared Workers and Module Workers have slightly narrower support—check caniuse.com before relying on them in production.
96%Modern browser support
Google ChromeAll versions · Desktop & Mobile
Full support
Mozilla FirefoxAll versions · Desktop & Mobile
Full support
Apple SafariAll versions · macOS & iOS
Full support
Microsoft EdgeAll versions · Chromium & Legacy
Full support
Internet ExplorerIE 6+ · Legacy environments
Full support
OperaAll modern versions
Full support
Web Workers APIExcellent
Bottom line: Safe to use for performance-critical client-side work in 2026. Always feature-detect and offer a main-thread fallback.
Pro Tips
💡 Best Practices
✅ Do
Use workers for CPU-heavy tasks that risk blocking the UI
Keep messages small or use Transferables for large buffers
Handle onerror and show user-friendly status text
Terminate workers when jobs finish if they are one-shot
Design workers around pure data in / data out
Feature-detect before calling new Worker()
❌ Don’t
Access the DOM from inside a worker
Assume globals are shared between threads
Spawn dozens of workers for tiny calculations
Send functions or DOM nodes through postMessage
Forget to clean up Blob URLs after inline workers
Block the UI with the same loop you could move to a worker
Wrap Up
Conclusion
Web Workers are a great tool for improving web application performance by offloading resource-intensive tasks from the main thread. Create a worker with new Worker(), send jobs through postMessage, and handle results in onmessage while the UI stays smooth.
Understanding worker limitations (no DOM, separate globals) and messaging patterns helps you build responsive dashboards, editors, and offline-capable apps. Try the interactive demos below to see workers in action.
Use these points when adding background threads to your apps.
5
Core concepts
🚀01
Background
Own thread.
Model
💬02
postMessage
Talk safely.
API
🚫03
No DOM
Main updates UI.
Limit
⚡04
Performance
Stay smooth.
Why
🛠️05
onerror
Handle fails.
Reliability
❓ Frequently Asked Questions
Web Workers let you run JavaScript in a background thread separate from the main page thread. They are ideal for CPU-heavy tasks like parsing large JSON, image processing, or mathematical loops without freezing the UI.
No. Workers cannot read or modify the DOM, window, document, or parent objects. Send data to the main thread with postMessage() and update the page there.
Call new Worker(url) with the path to a .js file, or create an inline worker from a Blob URL for single-file demos. The worker script runs in its own global scope where self refers to the worker context.
Use worker.postMessage(data) from the main thread and self.postMessage(data) from the worker. Listen with worker.onmessage on the main side and self.onmessage inside the worker. Data is copied using the structured clone algorithm.
Dedicated Workers belong to one page (new Worker). Shared Workers can be shared across multiple tabs from the same origin (new SharedWorker). This tutorial focuses on Dedicated Workers—the most common type.
Yes. Dedicated Web Workers are supported in all current major browsers including Chrome, Firefox, Safari, and Edge. Always feature-detect with typeof Worker !== 'undefined' before creating one.
Did you know?
Web Workers inspired later APIs like Service Workers (offline caching) and the Worker constructor for WebAssembly modules. They all run off the main thread, but Web Workers remain the simplest way to parallelize plain JavaScript.