HTML Web Workers API

Intermediate
14 min read
Updated: Nov 2026
5 examples
Worker / postMessage

Definition and Usage

The Web Workers API runs a script in a background thread separate from your HTML page’s main thread. Heavy work (big loops, parsing, crunching) can finish without freezing clicks, typing, or animations on the page.

What does this mean for an HTML page?

The main thread owns the DOM: buttons, inputs, and layout. A page script creates a Worker, sends work with postMessage, and when the result returns, updates HTML elements. The worker never touches document directly.

How does messaging work?

Both sides call postMessage(data) and listen with onmessage. The payload is copied (structured clone), not shared by reference—safe plain objects and arrays travel either way.

When should you use workers on a site?

Long CPU tasks that would make the page feel stuck. Skip workers for tiny work that finishes in a few milliseconds. Prefer dedicated workers for one page; shared and service workers cover multi-tab and offline cases.

Same origin.

Worker scripts must be served from the same origin as the HTML page (or a Blob URL you create).

No DOM in the worker.

Do not call document.getElementById inside the worker. Return data; paint the UI on the main thread.

Workers can fetch.

Background threads may use fetch / XMLHttpRequest (with limits). They can also spawn same-origin child workers.

HTML / Script Syntax

Create a dedicated worker from a page script and exchange messages:

JS
var worker = new Worker("worker.js");

worker.postMessage({ n: 10 });
worker.onmessage = function (event) {
  document.getElementById("out").textContent = event.data;
};
worker.onerror = function (err) {
  console.error(err.message);
};
// worker.terminate();
JS
self.onmessage = function (event) {
  var n = event.data.n;
  postMessage(n * n);
};
HTML
<button type="button" id="start">Run in worker</button>
<p id="out"></p>
<script src="main.js"></script>

Core HTML Concepts

How workers relate to your webpage:

ConceptRole
Main (UI) threadParses HTML, handles events, updates the DOM
WorkerDedicated background thread for one page script
postMessage / onmessageSend and receive copied data between page and worker
event.dataPayload on the message event
onerrorRuntime errors inside the worker script
terminate()Stop the worker immediately from the page
SharedWorkerShared across same-origin windows / iframes
Service WorkerNetwork proxy for offline / push (different lifecycle)

Main thread vs worker

HTML page (main)Worker
DOM / documentYesNo
Update a <p>YesSend data back instead
Heavy CPU loopFreezes UIKeeps UI responsive
Global objectwindowself / worker scope

How to Use Workers with an HTML Page

Keep the UI on the page; push compute to the worker.

  1. Build HTML controls (button, status <p>).
  2. Feature-detect: typeof Worker !== "undefined".
  3. Create new Worker("worker.js") (same origin).
  4. On click, worker.postMessage(input).
  5. In worker.onmessage, write event.data into the HTML.
  6. Handle onerror; call terminate() when the page no longer needs the worker.
GoalPattern
Start workernew Worker("worker.js")
Send workworker.postMessage(value)
Show result in HTMLworker.onmessage = (e) => el.textContent = e.data
Stop workerworker.terminate()
Inline demo workernew Worker(URL.createObjectURL(new Blob([code])))

Live Preview

Click to square a number in a background worker, then show the result in the HTML below.

Creates a Blob worker, posts n, and writes n × n into the page.

Result

Not started.

worker.postMessage(n) → onmessage → update <p>

HTML Examples

Five patterns: create a worker, message round-trip, errors, heavy loop, and a full HTML page. Try It demos use Blob workers so they run without a separate worker.js file.

Example 1 — Create a Web Worker

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

How It Works

  1. Feature-detect before constructing a worker.
  2. new Worker(url) loads a same-origin script into a background thread.
  3. HTML page keeps owning the DOM while the worker starts.
  4. Production usually uses a real .js file path, not only Blob URLs.

Example 2 — Send and Receive Messages

Main thread sends 5; worker returns 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

How It Works

  1. postMessage from the page starts the job.
  2. self.onmessage in the worker reads event.data.
  3. Worker replies with postMessage; the page listens on worker.onmessage.
  4. Next step — put the result into an HTML element instead of console.log.

Example 3 — Handle Worker Errors

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

How It Works

  1. onerror fires for uncaught exceptions in the worker script.
  2. Show status in the HTML UI so users know the job failed.
  3. Also validate inputs before posting to avoid predictable failures.
  4. terminate a broken worker if you plan to create a fresh one.

Example 4 — Offload a Heavy Loop

Sum many numbers off the main thread so the page stays clickable:

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

How It Works

  1. CPU work runs in the worker while the HTML UI thread stays free.
  2. Try It demos often keep a ticking counter to prove the page is not frozen.
  3. Return one number (or a small object) via postMessage.
  4. Update the page only after the result arrives.

Example 5 — Full HTML Page

Button sends 10; worker returns 100. Worker code lives in a Blob for a single file:

HTML
<button type="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

How It Works

  1. HTML provides the control and result element.
  2. Blob Worker embeds the worker script for demos / Try It.
  3. Click posts work; onmessage fills the paragraph.
  4. Sites in production usually keep worker.js as a separate file.

Edge Cases & Pitfalls

Most worker bugs are DOM access from the wrong thread or wrong script URLs.

dom

Touching the DOM in the worker

Fails. Return data with postMessage and update HTML on the main thread.

path

Wrong worker script URL

Must be same-origin and reachable. Check Network tab if the worker never starts.

clone

Non-cloneable values

Functions and DOM nodes cannot be posted. Send plain data (numbers, strings, plain objects).

leak

Never terminating workers

Call terminate() when leaving a page or finishing a one-shot job if you create many workers.

file

file: pages

Some browsers restrict workers from local files. Serve HTML over http(s) while learning.

overuse

Worker for tiny tasks

Startup and messaging cost can exceed the work. Use workers for truly heavy jobs.

Browser Support

Dedicated Web Workers are supported in all modern browsers. Always feature-detect before creating one.

✓ Baseline · Widely available

Workers for HTML pages

Background threads via new Worker() work across current browsers for same-origin page scripts.

100% Dedicated Worker
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
Web Workers Universal

Bottom line: Use dedicated workers to protect HTML UI responsiveness—feature-detect and keep DOM updates on the main thread.

Key Takeaways

  • Workers run heavy JS off the HTML page’s UI thread.
  • No DOM in the worker—update HTML after onmessage.
  • postMessage both ways; data is copied, not shared.
  • Feature-detect and prefer same-origin worker.js files in production.

One line: Page script creates a Worker → posts work → receives a result → updates the HTML—UI stays responsive.

Frequently Asked Questions

Web Workers run a JavaScript file in a background thread separate from the page’s main (UI) thread. Heavy work can finish without freezing buttons, scrolling, or animations on the HTML page.
No. Workers cannot access document, window, or parent. Send results with postMessage; the main page script updates HTML elements.
In a page script, call new Worker("worker.js") with a same-origin script URL. For single-file demos you can also create a Blob URL. Feature-detect with typeof Worker !== "undefined".
Both sides use postMessage to send data and onmessage to receive it. Data is copied (structured clone), not shared by reference.
Dedicated workers (one page), shared workers (several same-origin contexts), and service workers (network proxy / offline). This tutorial focuses on dedicated workers for HTML pages.
Use them for CPU-heavy tasks—large loops, parsing big files, image crunching—while the HTML UI must stay responsive. Skip them for tiny one-liners that finish instantly.

Practice in the editor

Open a Try It demo and run a Blob worker from a full HTML page.

Open Try It editor →

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.

8 people found this page helpful