completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Agents, agent clusters & realms

Learn how JavaScript agents, agent clusters, realms, intrinsics, shared memory, and the ShadowRealm proposal shape threads and globals.

By the end, you can
  • 01
    Tell three words apartSay what an agent, an agent cluster, and a realm each are, and which question each one answers.
  • 02
    Check values from another realmPredict when instanceof Array says false for a real array, and use Array.isArray or plain copied data instead.
  • 03
    Know what agents can shareSay when agents may share bytes with a SharedArrayBuffer, and why a new realm does not mean a new thread.

Three words that sound alike

This lesson is about three words. They sound alike, but each one answers a different question:

  • An agent is the thing that runs your JavaScript. It answers: who runs this code?
  • An agent cluster is a group of agents. It answers: which agents may share some things?
  • A realm is one set of global things: one globalThis and one copy of the built-ins, like Array. It answers: which Array does this code use?
Definition

An agent has three parts:

  • a call stack: the functions running right now,
  • a job queue: the work waiting to start,
  • one or more realms.

A realm holds a global object, globalThis. It also holds its own built-in values, such as Array, Object, and Promise. The spec calls these built-in values intrinsics.

This lesson comes after Modules through a bundler. The next lesson, How workers work, shows what it costs to start a new agent and to send data to it.

What an agent is

An agent is the thing that runs JavaScript. It runs one job at a time. Each agent has its own:

  • call stack: the functions running right now,
  • job queue: the work waiting to start,
  • realms: its sets of global things.

The page in a browser tab is one agent. Each worker is another agent. A worker is a script that runs beside the page, on its own.

Check two global namesPop out in the code editor (opens in a new tab)JavaScript
console.log(typeof globalThis, typeof Array);

Line 1 uses typeof. It gives a word that says what kind of value a name holds. The line prints object function:

  • globalThis is an object.
  • Array is a function that makes arrays.

This only tells you about the agent that runs the line. A worker has its own globalThis, so this line says nothing about it.

Real-life analogyTeachers and classrooms

Think of a school. Each teacher has a timetable. A teacher answers one question at a time. A teacher can teach in more than one classroom. Each classroom has its own copy of the textbooks.

In real life: A teacher answers one question at a time
In JavaScript: An agent runs one job at a time
In real life: A teacher has a timetable
In JavaScript: An agent has a call stack and a job queue
In real life: Each classroom has its own textbooks
In JavaScript: Each realm has its own built-ins, like Array

Two agents can run at the same time when the computer gives each one its own thread. A thread is a separate line of work that the computer runs alongside others. Even so, the main rule is simple: normal code in a worker does not share globalThis with the page.

Two windows, page and worker. Each holds its own box called globalThis. The page's box holds schoolName. The worker's box is empty, so schoolName is undefined there.pageschoolNameglobalThisschoolName is setworkerglobalThisschoolName is undefined
The page and the worker are two agents. Each one has its own globalThis, so a name the page sets is not there in the worker.

Agent clusters and shared memory

An agent cluster is a group of agents that may share some things. A page and the workers it starts can be in one cluster.

Most values you send to a worker are copied. The browser makes a fresh copy with a method called structured clone. The worker gets the copy, not your original.

A SharedArrayBuffer is different. It is a block of raw bytes that several agents can use together, when the browser allows it. If one agent changes a byte, the others see the change.

Two views look at the same bytesJavaScript
const bytes = new SharedArrayBuffer(4);
const pageView = new Int32Array(bytes);
const workerView = new Int32Array(bytes);
pageView[0] = 3;
console.log(workerView[0]);

The code reads the bytes through a typed array. A typed array, like Int32Array, is a view that reads bytes as numbers. Line by line:

  • Line 1 makes 4 shared bytes.
  • Lines 2 and 3 make two views of those same bytes.
  • Line 4 writes 3 through pageView.
  • Line 5 reads through workerView and prints 3.

This code has no Run button. The lesson editor can't turn on the safety settings that SharedArrayBuffer needs.

A box called bytes holds the number 3. A tag called pageView on the left and a tag called workerView on the right both point at the same box. pageView writes 3 and workerView reads 3.pageView[0] = 3pageViewwrites 33bytesthe same 4 bytesworkerView[0]workerViewreads 3
Both views look at the same four bytes. Line 4 writes 3 through pageView, so line 5 reads 3 through workerView.

Browsers usually let a page use SharedArrayBuffer only when the page is cross-origin isolated. That means the page sends two safety headers. They keep content from other websites out of the page unless that content agrees.

Sharing bytes does not make teamwork safe on its own. When two agents read and write the same bytes, they use Atomics. Atomics is a set of functions that do each read or write as one whole step, so the agents agree.

Step through one write to shared memory
Step 0 of 4Ready
Your turn: follow the blue line

Step through one write to shared memory. A real SharedArrayBuffer also needs the browser's safety settings. When agents take turns writing, they also need Atomics.

Running in
  1. script
Next: line 1
const workerAgent = { cluster: "school", memory: pageAgent.memory }; pageAgent.memory[0] = 3;console.log(workerAgent.memory[0]);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Realms and their built-ins

A realm is one set of global things. It has its own globalThis and its own built-ins, the intrinsics.

An iframe is a common example. An iframe is a small page shown inside another page. It has its own:

  • globalThis,
  • Array,
  • Object.prototype, the object where all plain objects find shared methods,
  • Promise.
Compare the page's Array with the iframe's ArrayJavaScript
const frame = document.createElement("iframe");
document.body.append(frame);

const frameArray = new frame.contentWindow.Array("tea");
console.log(frameArray instanceof Array);
console.log(Array.isArray(frameArray));
console.log(frame.contentWindow.Array === Array);

Every array is linked to an object called Array.prototype. That is where shared methods, like push, live. Line by line:

  • Lines 1 and 2 make an iframe and add it to the page.
  • Line 4 makes an array with the iframe's own Array.
  • Line 5 prints false. The array is linked to the iframe's Array.prototype, not to the page's.
  • Line 6 prints true. Array.isArray sees that the value is an array.
  • Line 7 prints false. The iframe's Array and the page's Array are two different functions.
Two carts, page and iframe. Each cart holds its own Array card and its own Object card. Between them: not the same Array.one realmArrayObjectpagenot thesame Arrayanother realmArrayObjectiframe
The page and the iframe are two realms. Each one starts with its own built-ins, like Array and Object. So line 7 prints false.

This code has no Run button. Try it in a normal page, with an iframe from the same website. The lesson editor runs code in a locked-down frame, so it can't read the iframe's contentWindow, which is the iframe's own globalThis.

A realm is not a thread. One agent can have several realms. So a new iframe gives you a new Array, but the iframe's code does not run at the same time as the page.

Checking a value from another realm

value instanceof Array asks a yes-or-no question: is this value linked to this realm's Array.prototype? It follows the value's chain of linked prototypes to find out.

Inside one realm, that works. But an array from an iframe is linked to the iframe's Array.prototype, so the page's check says no. That makes instanceof a weak check for values that come from an iframe or a worker.

An array card holding "tea", made in the iframe. One arrow leads to frameArray instanceof Array, which gives false. Another arrow leads to Array.isArray(frameArray), which gives true.["tea"]made in iframeframeArray instanceof ArrayfalseArray.isArray(frameArray)true
The array was made in the iframe. The page's instanceof Array check says false, but Array.isArray says true, because it is still an array.
Step through a check on an array from another realm
Step 0 of 5Ready
Your turn: follow the blue line

Step through a check on an array from another realm. instanceof depends on the realm. Array.isArray doesn't.

Running in
  1. script
Next: line 1
const frameArray = { realm: "frame", values: ["tea"] }; function check(value) {  return {    instanceofArray: value.realm === "page",    isArray: Array.isArray(value.values),  };} console.log(check(frameArray).instanceofArray, check(frameArray).isArray);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The replay uses a small model, not a real iframe. The model marks one array as made in the frame, so its instanceof Array result is false. Array.isArray still says true. A real iframe array gives the same two answers.

The table lists checks that look alike. Read each row as: "when I write this check, it depends on this, and here is a safer choice."

Checks that look alike, and safer choices
When you writeWhat it depends onA safer choice
value instanceof ArrayThis realm's Array.prototype. An array from an iframe gives false.Array.isArray(value)
value.constructor === ArrayThis realm's Array function. An array from an iframe gives false.Array.isArray(value)
A check on the value's prototypeThe prototypes of one realm. Each realm has its own.Check the fields you expect, or use Object.prototype.toString.call(value).
Sending your own objectsThe sender's objects and prototypes. The other side gets a copy, not your object.Send plain data. The browser copies it with structured clone.

One symbol list for the whole cluster

Each realm has its own built-ins. But a few things are shared by the whole cluster. One of them is the global symbol registry.

  • A symbol is a unique value. It is often used as a property key.
  • The global symbol registry is a list of symbols. Each symbol is saved under a name.
  • Symbol.for("cart") looks up the name cart in that list. If the name is not there yet, it adds a new symbol.
  • Plain Symbol() always makes a brand-new symbol. It never uses the list.
Ask the symbol list for the cart symbolJavaScript
const frame = document.createElement("iframe");
document.body.append(frame);

const pageKey = Symbol.for("cart");
const frameKey = frame.contentWindow.Symbol.for("cart");
console.log(pageKey === frameKey);
  • Lines 1 and 2 make an iframe and add it to the page.
  • Line 4 asks the list for cart from the page.
  • Line 5 asks the list for cart from the iframe.
  • Line 6 prints true. The page and the iframe are in the same cluster, so they share one list and get the same symbol.

This is one shared list. It does not mean every object is shared.

This code has no Run button. Try it in a normal page, with an iframe from the same website. The lesson editor's locked-down frame can't read the iframe's contentWindow.

Don't use this list to send data between realms. Send data on purpose, for example in a message. Use Symbol.for only when you need the same symbol in each realm.

The ShadowRealm proposal

ShadowRealm is a proposed new feature. TC39, the group that decides what goes into JavaScript, is still working on it. It would let your code make a fresh realm inside the current agent:

  • The new realm would have its own globalThis and its own built-ins.
  • It would not get a new thread.
Check that ShadowRealm exists before using itPop out in the code editor (opens in a new tab)JavaScript
if (typeof ShadowRealm === "function") {
  const realm = new ShadowRealm();
  console.log("available");
} else {
  console.log("not available");
}

Line 1 checks that ShadowRealm exists before line 2 uses it. This is called feature detection: you test for a feature before you use it. If the check fails, line 5 prints not available.

In September 2026, the proposal's page says Stage 2.7. A stage shows how far a proposal has got. It does not mean browsers have the feature. So this lesson never says it is available, and the example checks first.

ShadowRealm's goal is to keep one set of globals and built-ins apart from another, inside the same agent. It does not replace workers or the browser's rules about other websites. It is not a safe place to run code you don't trust.

Try it: one array, two realms

Change one thing at a time:

  1. Pick the realm that makes an array.
  2. Pick the realm that checks it.
  3. Choose whether the two agents are in one cluster. Only agents in one cluster may share memory.
Playground: where is the array made, and where is it checked?
The model's checksJavaScript
const sameRealm = madeIn === checkedIn;
console.log(sameRealm);       // like instanceof Array
console.log(true);            // like Array.isArray
console.log(true);            // like comparing two Symbol.for("cart")
console.log(sameCluster);     // may the agents share memory?
Result

instanceof Array: false

Array.isArray: true

Symbol.for("cart") === Symbol.for("cart"): true

Shared memory allowed: true

Try it yourself

One realm makes the array and another realm checks it, so instanceof Array is false. Array.isArray is true, and the Symbol.for check is true. The agents are in one cluster, so they may share memory.

This is a small model of the rules. It doesn't start real workers or look at real memory.

When the two realms are different, instanceof Array is false, but Array.isArray stays true. The Symbol.for result stays true, because the realms share one symbol list. When you untick the cluster box, shared memory is no longer allowed.

Where you meet this in real code

Most apps meet agents and realms in one of these places:

  • an iframe on your page,
  • a worker,
  • a test tool that runs code in a fresh realm (Node calls this a VM context),
  • a library that gets values from other code.

The safe habit is the same in each place. Send plain data, like strings, numbers, arrays, and plain objects. Write down the shape each message should have. Structured clone copies plain data well, and the copy doesn't depend on the sender's prototypes.

Node shows that a second realm has its own ArrayJavaScript
import vm from "node:vm";

const list = vm.runInNewContext("[]");
console.log(list instanceof Array);
console.log(Array.isArray(list));
console.log(vm.runInNewContext("Symbol.for('cart')") === Symbol.for("cart"));

This code runs in Node, not in the browser. vm.runInNewContext runs a piece of code in a fresh realm and hands back the result. Line by line:

  • Line 3 makes an empty array in the new realm.
  • Line 4 prints false. The array is linked to the new realm's Array.prototype.
  • Line 5 prints true. Array.isArray still sees an array.
  • Line 6 prints true. Both realms get the same symbol for cart, because they share one symbol list.
A worker has its own globalThisJavaScript
import { Worker, isMainThread, parentPort } from "node:worker_threads";

if (isMainThread) {
  globalThis.schoolName = "Page school";
  new Worker(new URL(import.meta.url)).once("message", console.log);
} else {
  parentPort.postMessage(globalThis.schoolName === undefined);
}

This Node file runs twice: once as the main program, and once as a worker. isMainThread is true only in the main program.

  • Line 4: the main program sets globalThis.schoolName.
  • Line 5: it starts a worker from the same file. It prints the first message the worker sends.
  • Line 7: the worker checks whether schoolName is undefined in its own globalThis, and sends the answer.

The program prints true. The worker is a separate agent with its own globalThis, as in the picture of the page and the worker.

Sort the words by layer
  • A worker's own call stack and job queue
  • A page running one job at a time
  • Letting two agents use one SharedArrayBuffer
  • The symbol list that Symbol.for uses
  • An iframe's own Array
  • One globalThis and the names on it
Try it yourself
0 of 6 correct

Put each card under the word it describes: agent, agent cluster, or realm.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mistakes

  • “A realm is a thread.” A realm is one set of global things. A worker is an agent, and it has its own realm.
  • “Agents in a cluster share every value.” Normal messages are copied. Only special tools, like SharedArrayBuffer, share bytes.
  • “instanceof works the same for every value.” It looks for one realm's prototype, so it can say no to a real array from another realm.
  • “Every browser has ShadowRealm.” It is still a proposal, at Stage 2.7. Check that it exists before you use it.

Practice exercises

Exercise 1 · Warm-upPredict the first example

Type the two words that the first example prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log(typeof globalThis, typeof Array);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upPick a check that works across realms

    An iframe sends your page an array. Name the check that still works.

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeIs a worker its own agent?

      Is a worker a separate agent from the page? Answer yes or no.

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeTurn on shared memory

        What must a page usually be before a browser lets it use SharedArrayBuffer?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeName the proposal stage

          Which proposal stage is ShadowRealm at?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeSend data to a worker

            Your app sends a cart summary to a worker. Which way of copying plain data should it use?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              Keep the three words apart. Agents run code. Clusters decide what agents may share. Realms hold the globals and built-ins.

              Agents and realms quiz · 7 questionsScore: first tries count
              1. Question 1 of 7Which description fits a JavaScript agent best?

                Choose an answer to see the explanation.

              2. Question 2 of 7What does this print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const frameArray = [];
                console.log(Array.isArray(frameArray));

                Choose an answer to see the explanation.

              3. Question 3 of 7Why can instanceof Array say false for an array from an iframe?

                Choose an answer to see the explanation.

              4. Question 4 of 7In one agent cluster, what do two Symbol.for("cart") calls return?

                Choose an answer to see the explanation.

              5. Question 5 of 7Which sentence about SharedArrayBuffer is true?

                Choose an answer to see the explanation.

              6. Question 6 of 7What does this lesson say about ShadowRealm?

                Choose an answer to see the explanation.

              7. Question 7 of 7You send normal data to a worker. Which choice doesn't depend on any realm's prototypes?

                Choose an answer to see the explanation.

              Key takeaways

              • An agent has a call stack, a job queue, and realms. The page and each worker are separate agents.
              • An agent cluster decides what agents may share, such as a SharedArrayBuffer.
              • Each realm has its own globalThis and its own built-ins, like Array.
              • To check for an array from another realm, use Array.isArray, not instanceof Array.
              • ShadowRealm is a Stage 2.7 proposal. Check that it exists before you use it.

              Remember the one-liner.
              Agents run code, clusters decide what is shared, and realms hold the globals and built-ins.

              Coming next: How workers work.

              Follow one agent's job queue

              • The call stack holds the functions that are running right now.
              • The job queue holds work that can start after the current job ends.

              An agent never runs two JavaScript jobs at the same moment. It finishes one job. Then it can take the next job from the queue.

              One agent takes one job from the queuePop out in the code editor (opens in a new tab)JavaScript
              const jobs = ["mark homework", "answer question"];
              const completed = [];
              
              completed.push(jobs.shift());
              console.log(completed.join(", "));
              console.log(jobs.join(", "));
              • Line 1 makes a list of two waiting jobs.
              • Line 2 makes an empty list of finished jobs.
              • Line 4 takes only the first waiting job and puts it in the finished list.
              • Line 5 prints mark homework.
              • Line 6 prints answer question, which is still waiting.

              This is a tiny model, not the browser's real job queue. It shows one rule: one agent does one job at a time. A page and a worker can make progress at the same moment only because they are two agents. Each has its own call stack, job queue, and globals.

              Sharing happens only when you ask for it

              When you send a normal object to a worker, the browser makes a copy with structured clone. The worker gets the same values. But it gets a new object, not your object. So two agents can't change the same cart object by accident.

              A plain message with a clear shapePop out in the code editor (opens in a new tab)JavaScript
              const message = { kind: "cart", items: ["tea"] };
              console.log(message.kind);
              console.log(Array.isArray(message.items));
              • Line 1 makes a plain message with a kind and a list of items.
              • Line 2 prints cart.
              • Line 3 prints true, because items is an array.

              A message like this is the easy, normal choice for your app. After structured clone copies it, items is still an array.

              SharedArrayBuffer is different on purpose. Agents that are allowed can make typed-array views of the very same bytes. In browsers, the page usually has to be cross-origin isolated first. See Shared memory for Atomics, and Browser security model for the safety settings.

              Each realm has its own built-ins

              An intrinsic is a built-in value that a realm starts with. Each realm has its own Array, Object, Object.prototype, and other built-ins. An array made on the page is linked to the page realm's versions.

              Check which Array an array is linked toPop out in the code editor (opens in a new tab)JavaScript
              const pageArray = ["tea"];
              console.log(pageArray.constructor === Array);
              console.log(Object.getPrototypeOf(pageArray) === Array.prototype);
              • Line 1 makes an array in this realm.
              • Line 2 prints true. The array's constructor, the function that made it, is this realm's Array.
              • Line 3 prints true. The array's prototype is this realm's Array.prototype.

              An iframe can run the same three lines and also get true twice. But its Array is not the page's Array. So a new realm gives you new built-ins, without a new thread. To learn more about global things, continue with Realms and globals.

              Always check for ShadowRealm first

              ShadowRealm is a proposal for a fresh realm inside the current agent. When this lesson was checked, the proposal's page said Stage 2.7. A stage says how far the design has got. It does not promise that a visitor's browser has the feature.

              Ask whether ShadowRealm exists firstPop out in the code editor (opens in a new tab)JavaScript
              if (typeof ShadowRealm === "function") {
                const realm = new ShadowRealm();
                console.log("available", typeof realm.evaluate);
              } else {
                console.log("not available");
              }
              • Line 1 checks for ShadowRealm. Using typeof here never throws, even when the name is missing.
              • If it exists, line 2 makes a realm, and line 3 prints available function.
              • If it doesn't, line 5 prints not available.

              The example is useful either way, so you can run it in any browser.

              A separate realm is not a worker. This proposal is also not a way to run code you don't trust. Workers, website origins, and clear message shapes each solve other problems. Here, the safe habit is simple: check that the feature exists before you use it.

              Fix a real iframe array check

              Imagine a shop page. A checkout iframe inside it sends the page an array of cart items. The page checks it with items instanceof Array.

              • In a test, this works, because the test makes the array in the same realm.
              • On the real page, it fails, because the iframe makes the array.
              Before: a cart check that depends on the realmPop out in the code editor (opens in a new tab)JavaScript
              function acceptsCart(items) {
                return items instanceof Array;
              }
              
              console.log(acceptsCart(["tea"]));

              Line 2 checks for this realm's Array.prototype. Line 5 prints true only because the array is made in the same realm. An array from an iframe would give false, even though it is still an array.

              After: a cart check that works in every realmPop out in the code editor (opens in a new tab)JavaScript
              function acceptsCart(items) {
                return Array.isArray(items);
              }
              
              console.log(acceptsCart(["tea"]));

              Line 2 uses Array.isArray, which sees arrays from any realm. Line 5 still prints true. The same function also accepts an array from the iframe.

              After that, check the cart's items too. Knowing it is an array is only the first step. You still need to check that each item has the shape you expect.

              Pick the check that fits your question

              Use instanceof when you want to know whether a value was made by one exact class, in your own realm. That works well for your own class objects. It is not a good check for data from an iframe, a VM context, or a library.

              Two array checks that work in every realmPop out in the code editor (opens in a new tab)JavaScript
              const value = ["tea"];
              console.log(Array.isArray(value));
              console.log(Object.prototype.toString.call(value));
              • Line 1 makes the example array.
              • Line 2 prints true. Array.isArray is the best check for arrays.
              • Line 3 prints [object Array]. Object.prototype.toString.call gives a short text label for any built-in kind of value.

              For arrays, use Array.isArray, because it asks exactly the question you mean. Use the text label when you are debugging and want to know what kind of value you have. Avoid value.constructor === Array. It compares with one realm's Array, so it fails for the same reason as instanceof.

              CompleteFrontend Clear concepts. Working examples.