cf.completefrontendCode editorOpen lab
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
    Separate three layersExplain how an agent, its cluster, and its realms answer different questions about JavaScript execution.
  • 02
    Choose safe boundary checksPredict cross-realm instanceof behavior and use Array.isArray or structured cloning when needed.
  • 03
    Evaluate sharing claimsTell when agents can share SharedArrayBuffer memory and why a fresh realm is not a new thread.

A map for threads and globals

JavaScript has a few words that sound alike but answer different questions. An agent explains who executes JavaScript. An agent cluster explains which agents may share selected state. A realm explains which global object and built-in constructors a piece of code uses.

Definition

contentWindow. An agent owns a stack, a job queue, and one or more realms. A realm owns a global environment and intrinsics such as Array, Object, and Promise.

This lesson follows Modules through a bundler. It prepares you for How workers work, where the cost of creating agents and sending data becomes practical.

What an agent is

In plain words, an agent is one teacher working through one thing at a time. It has its own call stack, its own job queue, and a set of classrooms. A browser window is an agent, and each worker is another agent.

Ask for two globalsPop out in the code editor (opens in a new tab)JavaScript
console.log(typeof globalThis, typeof Array);

Line 1 asks what two names are. It prints object function: globalThis is an object, and Array is a constructor function. This says nothing about another worker, because that worker has its own global environment.

Real-life analogyTeachers and classrooms

Think of a school. Each teacher has a schedule and talks through one question at a time. The teacher can use more than one classroom, and each classroom has its own copy of the textbooks.

In real life: One teacher handles one question
In JavaScript: One agent runs one job at a time
In real life: A teacher has a schedule
In JavaScript: An agent has a stack and queue
In real life: Classrooms have their own textbooks
In JavaScript: Realms have their own intrinsics

Where the analogy stops: A real agent can run many jobs over time; the analogy does not model scheduling details.

Agents can run in parallel when the host provides separate threads. The important boundary is still simple: ordinary code in one worker does not share its globalThis with the page.

Agent clusters and shared memory

An agent cluster groups agents that are allowed to share selected things. Most values passed to a worker are copied with structured clone. A SharedArrayBuffer is different: multiple agents can view the same bytes when the browser allows it.

Two views of shared bytesJavaScript
const bytes = new SharedArrayBuffer(4);
const pageView = new Int32Array(bytes);
const workerView = new Int32Array(bytes);
pageView[0] = 3;
console.log(workerView[0]);

Line 1 allocates four shared bytes. Lines 2 and 3 make two views of those bytes. Line 4 writes 3, so line 5 prints 3. This is deliberately marked Node or worker-style teaching code because ordinary page sandbox examples cannot promise the needed isolation headers.

Browsers generally require cross-origin isolation before exposing SharedArrayBuffer. Sharing bytes also does not make coordination safe; agents use Atomics when reads and writes must agree.

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

Replay a shared-memory teaching model. Real SharedArrayBuffer use needs the relevant browser security requirements and synchronization with Atomics when agents coordinate writes.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const workerAgent = { cluster: "school", memory: pageAgent.memory }; pageAgent.memory[0] = 3;console.log(workerAgent.memory[0]);
CallStoreChangeResultRun = next line. 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 intrinsics

A realm is a global environment with its own intrinsic objects. An iframe is a familiar browser example: it has another globalThis, another Array, another Object.prototype, and another Promise.

Compare the page and iframe Array constructorsJavaScript
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);

Line 1 creates an iframe. Line 4 makes an array using the iframe realm's constructor. Line 5 prints false because that array does not inherit from the page's Array.prototype. Line 6 prints true, and line 7 prints false.

Run this in a normal page with a same-origin iframe. The lesson editor's opaque-origin sandbox cannot read properties from the frame's contentWindow.

A realm is not a thread. One agent can host several realms. The distinction matters because a new iframe can change constructor identity without promising work runs on another CPU.

Cross-realm checks

instanceof follows a prototype chain and asks whether a specific prototype occurs in it. That works inside one realm, but it is a fragile type check at an iframe or worker-facing boundary.

Step through a cross-realm array check
Step 0 of 5Ready
Your turn: follow the blue line

Replay a cross-realm check. It shows why prototype-based checks depend on the realm that supplied the constructor.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const frameArray = ["tea"]; // made with the frame's Array function check(value) {  return {    instanceofArray: value.realm === "page",    isArray: true,  };} console.log(check({ realm: "frame", value: frameArray }));
CallStoreChangeResultRun = next line. 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 walks an array made in a second realm. The page's Array is not in that array's prototype chain, so the model returns false for instanceof. Array.isArray instead checks the array nature of the value, so it returns true.

Boundary checks that look similar
CheckWhat it depends onSafer boundary choice
value instanceof ArrayIt compares against this realm's Array.prototype chain.A value made in an iframe can return false.
value.constructor === ArrayIt compares constructors from a particular realm.Use Array.isArray(value) for array recognition.
Prototype checksThey depend on realm-owned prototypes.Use a documented data contract or Object.prototype.toString.call(value).
Passing mutable objectsIdentity and prototype assumptions travel poorly across a boundary.Use structured cloning or explicit serialization at a worker boundary.

A shared symbol registry

Realms have separate intrinsics, but not every identity is realm-local. The global symbol registry is associated with an agent cluster. That makes Symbol.for useful for a shared key, while plain Symbol() always creates a fresh symbol.

Ask the registry for the cart keyJavaScript
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);

Line 4 asks the page registry for cart. Line 5 asks the iframe's realm in the same cluster for the same key. Line 6 prints true. This does not mean all objects are shared; it is one registry rule.

Run this in a normal page with a same-origin iframe; the editor's opaque-origin sandbox blocks access to the frame's contentWindow.

Do not use this fact as a data channel. Send data explicitly, and use symbols only when symbol identity is the intent.

The ShadowRealm proposal

ShadowRealm is a TC39 proposal for creating a fresh realm inside the current agent. It gives that realm a separate global environment and separate intrinsics without claiming a new thread.

Feature-detect the proposalPop 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 before use. In September 2026 the proposal repository reports Stage 2.7. That is a proposal stage, not a guarantee that browsers ship the API, so this lesson never claims availability and the example feature-detects it.

Its goal is isolation of globals and intrinsics within the same agent. It is not a replacement for workers, cross-origin policy, or a security sandbox for untrusted code.

Two teachers, two classrooms

Use this model to change one boundary at a time. Pick the classroom that makes an array, then the classroom that checks it. Finally decide whether the two teachers are in the same building, which represents a cluster that may share memory.

Playground: choose where a value is made and checked
The model's cross-realm checksJavaScript
const sameRealm = madeIn === checkedIn;
console.log(sameRealm);       // instanceof Array model
console.log(true);            // Array.isArray model
console.log(true);            // Symbol.for cart key model
console.log(sameCluster);     // shared-memory permission model
Result

instanceof Array: false

Array.isArray: true

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

Shared memory allowed: true

Try it yourself

An array from classroom-b checked in classroom-a makes instanceof Array false, Array.isArray true, Symbol.for equality true, and SharedArrayBuffer sharing true.

This small model teaches the boundary rules. It does not create browser workers or inspect browser memory.

When classrooms differ, the model shows instanceof Array as false while Array.isArray remains true. Its symbol result stays true because the global symbol registry belongs to the modeled cluster. Turning off the shared building removes shared-memory permission.

Practical boundaries

Most app code meets this lesson at an iframe, a worker, a testing VM context, or a library that receives values from elsewhere. Start with plain data and a documented message shape. Structured cloning makes the boundary visible and avoids inherited prototype surprises.

Node proves realm identity is realJavaScript
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"));

Node's vm.createContext creates a fresh realm. The three logs are false, true, and true. The first two show the same iframe lesson without a browser; the third verifies the cluster-level symbol registry behavior.

A worker has another globalJavaScript
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);
}

The page agent sets a global, but the worker reports true for “is it undefined?”. That is stable Node evidence that a worker has a separate global environment and agent.

Sort the runtime words
  • A worker's own call stack and job queue
  • A window handling one task at a time
  • Permission for two agents to use one SharedArrayBuffer
  • The global symbol registry shared by related realms
  • An iframe's separate Array constructor
  • One global object and its bindings
Try it yourself
0 of 6 correct

Put each card beside the layer it describes.

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 a global environment. A worker is an agent with its own realm.
  • “Every value is shared in a cluster.” Normal messaging copies data; only explicit shared-memory mechanisms share bytes.
  • “instanceof names a universal type.” It depends on a prototype from one realm.
  • “ShadowRealm is a shipped browser primitive.” It is a Stage 2.7 proposal and code must feature-detect it.

Practice exercises

Exercise 1 · Warm-upPredict the first line

Type the two words printed by the first example.

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-upChoose a cross-realm check

    An iframe sends your page an array. Name the safe check.

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

      Exercise 3 · PracticeName the execution boundary

      Is a browser worker a separate agent from the window?

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

        Exercise 4 · PracticeEnable the right prerequisite

        Name the browser condition commonly required before SharedArrayBuffer is available.

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

          Exercise 5 · ChallengeRead a proposal status

          What current proposal stage should you report for ShadowRealm?

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

            Exercise 6 · ChallengeApply it to a real website

            Your app sends a cart summary to a worker. What boundary technique should it use for ordinary data?

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

              Check your understanding

              Keep the layers separate: agents execute, clusters decide sharing, and realms provide globals and intrinsics.

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

                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 fail for an iframe array?

                Choose an answer to see the explanation.

              4. Question 4 of 7What does two Symbol.for("cart") calls in the same agent cluster return?

                Choose an answer to see the explanation.

              5. Question 5 of 7Which statement about SharedArrayBuffer is accurate?

                Choose an answer to see the explanation.

              6. Question 6 of 7What is the status of ShadowRealm in this lesson?

                Choose an answer to see the explanation.

              7. Question 7 of 7Which ordinary worker-boundary choice avoids cross-realm prototype assumptions?

                Choose an answer to see the explanation.

              Key takeaways

              • An agent has a stack, a job queue, and realms; a window and worker are separate agents.
              • An agent cluster controls selected shared state, including SharedArrayBuffer eligibility.
              • Each realm has its own global object and intrinsics.
              • Use Array.isArray instead of realm-sensitive instanceof at array boundaries.
              • ShadowRealm is a Stage 2.7 proposal: feature-detect it and do not assume browser support.

              Remember the one-liner.
              Agents execute, clusters govern sharing, and realms own globals and intrinsics.

              Coming next: How workers work.

              Follow one agent's queue

              A stack is where the current nested calls live. A job queue holds work that can start after the current job finishes. The agent does not run two JavaScript jobs at the same instant. It finishes one turn, then the host can choose another queued job.

              One teacher takes one queued jobPop 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 creates two waiting jobs. Line 2 creates an empty completed list. Line 4 removes only the first waiting job and saves it. Line 5 prints mark homework. Line 6 prints answer question, which is still waiting.

              This is a tiny teaching model, not a browser event-loop implementation. Its important promise is modest: one agent progresses one job at a time. A window and a worker can make progress in parallel because the host gives them separate agents, stacks, queues, and globals.

              Sharing is selected, not automatic

              Sending a normal object to a worker normally creates a copy through structured clone. The receiver gets data with the same useful values, but it does not receive the sender's ordinary object identity. That rule prevents two agents from silently changing the same cart object.

              A plain message has 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 item list. Line 2 prints cart. Line 3 prints true because the copied items value is still an array. This is the easy default for application messages.

              SharedArrayBuffer is deliberately different: permitted agents can make typed-array views over the exact same bytes. In browsers, cross-origin isolation is the security condition that usually makes this available. See Shared memory for Atomics, and Browser security model for the isolation boundary.

              Intrinsics belong to a realm

              An intrinsic is a built-in value a realm starts with. Each realm has its own Array, Object, Object.prototype, and other built-ins. A page array naturally points at the page realm's versions.

              Inspect this realm's Array linksPop 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 because its constructor is this realm's Array. Line 3 also prints true because its direct prototype is this realm's Array.prototype.

              An iframe can repeat those three lines and receive its own true results, yet its Array is not the page's Array. That is why realm identity can change without a new CPU thread. Continue with Realms and globals for a wider map of global environments.

              Feature detection is the ShadowRealm contract

              ShadowRealm is a proposal for a fresh realm in the current agent. The proposal repository reported TC39 Stage 2.7 when this lesson was verified. A proposal stage says where a design is in the standardization process; it does not promise that a visitor's browser implements it.

              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 tests the global without throwing. If it is present, line 2 creates a realm and line 3 prints available function. Otherwise line 5 prints not available. The runnable example is intentionally useful in both cases.

              A separate realm is not a worker, and this proposal is not an instruction to run untrusted strings. Workers, browser origins, content security policy, and explicit message contracts solve different problems. Treat feature detection as the safe public boundary here.

              Repair a real iframe type check

              Imagine a product page accepts a cart array from an embedded checkout iframe. The page author writes items instanceof Array. It works in a unit test because the test creates the array in the same realm. It breaks when the iframe creates the real array.

              Before: a realm-sensitive cart checkPop out in the code editor (opens in a new tab)JavaScript
              function acceptsCart(items) {
                return items instanceof Array;
              }
              
              console.log(acceptsCart(["tea"]));

              Line 2 compares against this realm's Array.prototype. Line 5 prints true only because the literal array is made in the same realm. An iframe array can make that exact check false even though it is still an array.

              After: recognize arrays across realmsPop 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 recognizes array values across realms. Line 5 still prints true, and the same function also accepts an iframe array. Validate the cart's fields after that; being an array is only the first part of a data contract.

              Choose the check for the question

              Use instanceof when you intentionally need a value built from one particular constructor in one controlled realm. It is useful for local class instances, but it is not a universal label for data arriving from an iframe, a VM context, or a library boundary.

              Two realm-safe array observationsPop 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 creates the example array. Line 2 prints true with the best dedicated array check. Line 3 prints [object Array], a more general built-in tag from Object.prototype.toString.call.

              Prefer Array.isArray for arrays because it states the exact question. Use the tag form when you need a broader diagnostic. Avoid value.constructor === Array: it compares another realm-owned identity and fails for the same reason as instanceof.

              CompleteFrontend Clear concepts. Working examples.