HTML Web Workers API

Beginner
⏱️ ~12 min
📚 Updated: Aug 2026
🎯 5 Examples
🚀 5 Try-it labs
Worker · postMessage · onmessage

What You’ll Learn

Web Workers run JavaScript on a background thread so heavy work does not freeze clicks, scroll, or animation on the main page. This tutorial covers creating a Worker, messaging with postMessage / onmessage, error handling, DOM limits, five examples (including inline Blob workers), and how Dedicated Workers compare to Shared Workers.

Worker()

Spawn a thread

Create a background script with new Worker(url) or an inline Blob URL.

postMessage

Send data

Pass jobs and results between the main thread and the worker.

onmessage

Receive results

Listen for replies on both sides of the messaging channel.

No DOM

Thread limits

Workers cannot touch document or window—update the UI on the main thread.

onerror

Catch failures

Surface worker runtime errors instead of failing silently.

terminate()

Clean up

Stop a worker when you no longer need it to free resources.

Introduction

Web Workers are a browser feature that runs JavaScript in background threads, separate from the main thread that paints the page and handles clicks.

Each worker has its own global scope (self) and communicates only through messages. That makes workers ideal for long calculations, parsing large files, and media processing—without freezing scroll or input.

Why it matters?

JavaScript on the main thread is single-threaded for UI work. A multi-second loop freezes the page. Workers move that CPU load off the UI thread so the app stays responsive.

Key Highlights

Background Threads

Heavy work runs off the main UI thread.

Message Passing

postMessage clones data between threads safely.

No DOM Access

Update the page only from the main thread.

Wide Support

Dedicated Workers work in all modern browsers.

In short: create a Worker, send jobs with postMessage, receive results with onmessage, never touch the DOM inside the worker, and call terminate() when done.

Creating a Web Worker

Always feature-detect, then create a worker from a script URL:

js
if (typeof Worker === 'undefined') {
  console.warn('Web Workers not supported');
} else {
  const worker = new Worker('worker.js');
}

In worker.js, listen and reply:

js
self.onmessage = function (event) {
  const result = event.data * 2;
  postMessage(result);
};

Inline worker (single-file demos)

In the Try It editor you often have one HTML file. Put worker code in a string and create a Blob URL:

js
var code = 'self.onmessage = function(e) { postMessage(e.data * 2); };';
var worker = new Worker(
  URL.createObjectURL(new Blob([code], { type: 'application/javascript' }))
);
Try It Yourself

Communicating with Web Workers

Send from the main thread; receive replies in onmessage:

js
worker.postMessage(5);
worker.onmessage = function (event) {
  console.log('Received from worker:', event.data); // 10
};

Inside the worker, mirror the pattern:

js
self.onmessage = function (event) {
  postMessage(event.data * event.data);
};

You can send plain objects: worker.postMessage({ action: 'sum', values: [1, 2, 3] }). The worker receives a deep copy (structured clone), not a shared reference.

⚡ Quick Reference

TaskCode pattern
Feature detectif (typeof Worker !== 'undefined') { ... }
Create workerconst w = new Worker('worker.js')
Send to workerw.postMessage(data)
Receive from workerw.onmessage = (e) => e.data
Worker listensself.onmessage = (e) => { ... }
Worker repliespostMessage(result)
Stop workerw.terminate()
Create
new Worker(url)

Spawn thread

Send
postMessage()

Both sides

Listen
onmessage

Event handler

No DOM
self only

Worker scope

📋 Dedicated vs Shared vs Main Thread

Choose the right place to run work.

Dedicated Worker
new Worker()

One page, one background thread—the common choice for this tutorial.

Shared Worker
new SharedWorker()

Shared across tabs of the same origin; more complex messaging.

Main thread
UI + light work

Best for DOM updates and short tasks; avoid multi-second loops here.

Context

When to Use Web Workers

Reach for a worker when CPU work risks freezing the UI.

  1. Heavy calculations

    Math loops, simulations, sorting large arrays.

  2. Parsing files

    JSON, CSV, and log files that take noticeable time.

  3. Media processing

    Image resize, audio analysis, waveform generation.

  4. Not for DOM work

    Workers cannot update elements—do that on the main thread.

Key benefit: the page stays interactive while expensive work finishes in the background.

Handling Errors

Catch runtime errors from the worker script with worker.onerror:

js
worker.onerror = function (error) {
  console.error('Worker error:', error.message);
  console.error('File:', error.filename, 'Line:', error.lineno);
};

Call worker.terminate() when finished to free memory. Create a new Worker if you need more jobs later.

Examples Gallery

Five starter demos. Use View Output to preview here, or open Try It Yourself to edit and run live (?tryit=1 through 5). Try Its use inline Blob workers so you do not need a separate worker.js file.

Example 1 — Create a Web Worker

Feature-detect, then create a worker (file URL in production; Blob in Try It).

js
if (typeof Worker === 'undefined') {
  alert('Web Workers not supported');
} else {
  var worker = new Worker('worker.js');
  console.log('Worker created');
}
Try It Yourself

Example 2 — Send and Receive Messages

Main thread sends 5. Worker doubles it and sends back 10.

js
worker.postMessage(5);
worker.onmessage = function (event) {
  console.log('Received from worker:', event.data); // 10
};
js
self.onmessage = function (event) {
  postMessage(event.data * 2);
};
Try It Yourself

Example 3 — Handle Worker Errors

Log runtime errors that occur inside the worker script.

js
worker.onerror = function (error) {
  console.error('Worker error:', error.message);
};
Try It Yourself

Example 4 — Offload a Heavy Loop

Sum many numbers in a worker so the page stays clickable. The Try It demo keeps a counter ticking while the worker runs.

js
self.onmessage = function (e) {
  var total = 0, i;
  for (i = 0; i < e.data; i++) total += i;
  postMessage(total);
};
Try It Yourself

Example 5 — Full HTML Page

Click sends 10; the worker returns 100. Worker code lives in a Blob string:

html
<button id="start">Start Worker</button>
<p id="result"></p>
<script>
  var worker = new Worker(URL.createObjectURL(new Blob([
    'self.onmessage=function(e){postMessage(e.data*e.data);};'
  ], { type: 'application/javascript' })));
  worker.onmessage = function (e) {
    document.getElementById('result').textContent = 'Result: ' + e.data;
  };
  document.getElementById('start').onclick = function () {
    worker.postMessage(10);
  };
</script>
Try It Yourself

🚀 Common Use Cases

  • Data crunching — aggregate analytics, sort large tables, or run simulations.
  • File parsing — read CSV, JSON, or logs without blocking typing or scrolling.
  • Media processing — resize images, decode audio, or build waveforms.
  • 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.

✨ Advantages

  • Keeps the UI thread free for input and animation
  • Uses real parallel CPU work in the browser
  • Clear messaging API with structured cloning
  • Supported across modern desktop and mobile browsers

💡 Usage Tips

  • Feature-detect Worker and provide a main-thread fallback for critical paths
  • Prefer Blob workers for single-file demos; use separate .js files in production
  • Send only the data you need—large clones are expensive
  • Use Transferable objects (ArrayBuffer) for zero-copy moves when transferring big buffers
  • Call terminate() when a worker is idle for a long time

Common Pitfalls

Avoid these mistakes when workers misbehave.

  1. 1. Touching the DOM from a worker

    Workers cannot access document or window.

    → Post results to the main thread and update the UI there.

  2. 2. Assuming shared memory by default

    Message data is cloned, not shared.

    → Expect copies; use Transferables when you must move large buffers.

  3. 3. Spawning workers for tiny tasks

    Startup and messaging overhead can outweigh the benefit.

    → Use workers when work risks blocking the UI for a noticeable time.

  4. 4. Forgetting to terminate

    Idle workers still consume resources.

    → Call terminate() when the job pipeline is done.

  5. 5. Skipping feature detection

    Some restricted environments may not expose Worker.

    → Check typeof Worker !== 'undefined' and fall back on the main thread.

Pro Tip: if messages never arrive, confirm both sides set onmessage before the first postMessage, and that the worker Blob/URL is valid.

🧠 How Web Workers Work

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
=

Result posted back

Main thread updates the DOM with the answer.

Important Notes

  • Workers cannot access the DOM—only message results back.
  • postMessage uses structured clone; functions and DOM nodes cannot be sent.
  • Dedicated Workers are the default; Shared Workers are for multi-tab coordination.
  • Same-origin rules apply to worker script URLs unless CORS allows otherwise.
  • Next: browse the ASCII Table reference, or revisit Server-Sent Events for streaming from the server.

Quick Takeaway: detect Worker, spawn a background script, exchange data with postMessage, keep the DOM on the main thread, and terminate when finished.

Browser Support

Dedicated Web Workers are a long-standing web standard supported in all modern browsers for background JavaScript threads.

Baseline

Web Workers

Use Dedicated Workers for CPU-heavy work. Feature-detect with typeof Worker !== 'undefined'. Shared Workers have slightly different support and APIs.

100% Modern browsers
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
Dedicated Worker Universal

Bottom line: Safe for production. Always feature-detect and keep a main-thread fallback for critical paths.

Wrap Up

🎉 Conclusion

Web Workers keep pages responsive by moving expensive JavaScript off the main thread. You create a worker, exchange messages, handle errors, and update the DOM only from the main script.

Practice the five examples above—especially the inline Blob pattern used in Try It—then continue to the ASCII Table reference or revisit streaming with Server-Sent Events.

Remember: no DOM inside the worker, clone costs on every message, and terminate when idle.

💡 Best Practices

✅ Do

  • Feature-detect Worker before creating one
  • Keep DOM updates on the main thread
  • Use Blob workers for single-file demos
  • Handle onerror and empty/failed replies
  • Terminate workers you no longer need
  • Prefer Transferables for large binary payloads

❌ Don’t

  • Access document or window from a worker
  • Spawn a worker for tiny one-line tasks
  • Send huge objects on every animation frame
  • Ignore message/serialization errors
  • Forget same-origin rules for worker scripts
  • Assume SharedWorker APIs match Dedicated Workers

Key Takeaways

Knowledge Unlocked

Five things to remember about Web Workers

Keep the UI smooth with background threads.

5
Core concepts
💬02

postMessage

Send jobs and results

Message
📝03

No DOM

Update UI on main thread

Limits
04

onerror

Surface worker failures

Errors
🚀05

Next: ASCII

Continue to the ASCII Table

Path

❓ 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 run in a separate thread with no access to the DOM. Use postMessage() to send data and onmessage to receive results—structured cloning copies plain objects safely between threads. Dedicated Workers shipped in browsers years before SharedArrayBuffer and Atomics—message passing remains the simplest, safest concurrency model for most apps.

Continue to ASCII Table

Browse character codes and related HTML entity references.

ASCII Table →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

9 people found this page helpful