> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kameleoon.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Engine execution flow

> Understand how the Kameleoon experimentation engine works, including a detailed breakdown of loading, tracking, and execution phases.

# Engine execution flow

Learn how the experimentation engine loads, tracks, and executes experiments.

## Experimentation engine overview

Kameleoon provides a single-platform optimization solution for web and full-stack application experimentation. The platform supports two primary solutions:

* **Web Experimentation**: Conduct A/B testing and other forms of experimentation for websites and web applications.
* **Feature Experimentation**: Enable full-stack feature flagging and experimentation. Kameleoon also provides additional APIs and tools to eliminate flickering in A/B experiments, integrate with your existing tools, and automate tasks.

### Code flow: loading

```mermaid theme={null}
graph TD
    %% Main Flow Nodes
    EntryPoint(["Entry point"])
    JSFile(["Kameleoon.js"])
    
    LoadEngine(["Load the Kameleoon engine"])
    InitEngine(["Initialize the Kameleoon engine"])
    
    EventLoaded("Send the Kameleoon::Loaded event")
    CheckAbort(["Check abort conditions"])
    
    %% Branching Outcomes
    NoCond("No")
    YesCond("Yes")
    
    %% Yes Flow Nodes
    EventAborted("Send the Kameleoon::Aborted event")
    StopExecution("Stop the execution of Kameleoon<br>on the webpage")
    
    %% Side-Car/Optional Nodes
    DetailCmdQueue("Execute the Kameleoon CommandQueue code<br>with the level: IMMEDIATE argument")
    DetailFlicker("Optional: Remove the anti-flicker CSS rule.<br>Store the timestamp of the engine initialize completion step.")
    DetailDefer(["Optional: Defer all campaigns and configurations<br>(goals, segments, and so on) in a second call."])
    
    %% Continuation Placeholder
    NoDown("⬇")

    %% --- Connections ---
    
    %% Main Path
    EntryPoint --> JSFile
    JSFile --> LoadEngine
    LoadEngine --> InitEngine
    InitEngine --> EventLoaded
    EventLoaded --> CheckAbort
    
    %% Conditions
    CheckAbort --> NoCond
    CheckAbort --> YesCond
    
    %% Yes Path
    YesCond --> EventAborted
    EventAborted --> StopExecution
    
    %% No Path
    NoCond --> NoDown
    
    %% Side Actions
    LoadEngine --> DetailCmdQueue
    InitEngine --> DetailFlicker
    
    %% Connect the two Optional blocks
    DetailFlicker --> DetailDefer

    %% --- Styling ---
    classDef default fill:#F2F2FF,stroke:#8A8AC2,stroke-width:1px,color:#111;
    classDef whiteBox fill:#FFFFFF,stroke:#E2E2F2,stroke-width:1px,color:#111;
    classDef borderless fill:none,stroke:none,color:#111;
    
    class EventLoaded,EventAborted,StopExecution,DetailCmdQueue,DetailFlicker,NoCond,YesCond whiteBox;
    class NoDown borderless;
```

The Kameleoon engine runs the `engine.js` tag (previously `kameleoon.js`). Invoke this JavaScript tag through the HTML source code of your application pages.

At the entry point, the engine:

1. **Activation**: The `engine.js` tag invokes an Activation API call through `Kameleoon.Analyst.load()`.

2. **Command queue execution**: After loading, the engine checks the [kameleoonQueue array](../../apis/activation-api-js/api-reference/command-queue#syntax) and executes commands in the order you pushed them. Use this for code that must run before Kameleoon instantiates functions.

<Note>
  Kameleoon only executes functions added to the queue with the `IMMEDIATE` parameter.
</Note>

3. **Anti-flicker and global variables**: **Flickering** occurs when the original page displays briefly before the variation appears. This effect can lead to inaccurate results. Kameleoon uses advanced techniques to eliminate flickering. If you configure anti-flicker, the system loads the Kameleoon script asynchronously with [anti-flicker code](../implementation-and-deployment/standard-implementation#asynchronous-tag-with-anti-flicker).

Once Kameleoon finishes loading, the `window.KameleoonEndLoadTime` variable stores the completion timestamp.

<Note>
  Adjust the timeout using the `kameleoonLoadingTimeout` variable in the installation snippet. We recommend keeping the default value of 1000 milliseconds (or 750 milliseconds for modern implementations). This variable determines the maximum time the installation tag can delay page display while waiting for the application file.
</Note>

4. **Check for blocking reasons**: Before completing initialization, the engine checks for these blocking reasons:

   * **Storage**: If local or session storage is unavailable, the engine terminates and returns the `Kameleoon::Aborted` event with the `STORAGE` code.
   * **Timeout**: If `engine.js` takes more than 750ms to load and you configured the project to disable Kameleoon for the page or visit, the engine returns `TIMEOUT`.
   * **Prerender**: Kameleoon requires the document to be ready (`document.visibilityState == "prerender"`). If it is not, the script terminates and returns `PRERENDER`. It reloads when the visibility state changes.
   * **Custom script**: If a custom script aborts the session, the engine returns `SCRIPT`.
   * **Parameter**: If the URL contains `KameleoonDisabled == true`, the engine returns `PARAMETER` and terminates.
   * **Disabled**: If you disable the project in the dashboard, the engine returns `DISABLED`. See [this article](/user-manual/project-management/manage-your-projects) for details.

<Note>
  Only a Kameleoon technical account manager can configure custom abortion scripts.
</Note>

5. **Deferrals**: The engine postpones code and campaigns for a second call if necessary. In this case, the engine fetches configuration and campaigns in SYNC mode from `https://SITECODE.kameleoon.com/engine-configuration.js` (previously named `kameleoon-configuration.js`) and `https://SITECODE.kameleoon.com/engine-campaigns.js` (previously named `kameleoon-actions.js`), respectively.

<Note>
  Only a Kameleoon technical account manager can configure the option to defer all campaigns. You can defer specific experiments using the "DELAYED" tag.
</Note>

If no blocking reasons exist, the engine proceeds to the visitor data tracking stage.

### Code flow: tracking

```mermaid theme={null}
graph TD
    %% --- MAIN SPINE ---
    ParseBrowser(["Parse the browser and OS"])
    UnifySession("If the Unify session data across subdomains option is enabled:")
    RetrieveData["Send a message to the Kameleoon iframe to retrieve the data from the main domain's local storage.<br>Otherwise, retrieve it from the current local storage."]
    InitConsent(["Initialize consent management"])
    
    %% MERGED NODE: InitVisitor + its details
    InitVisitor["Initialize the Visitor object<br>---<br>1. Obtain all previous visits from local storage.<br>Optional: If the Real-Time Sync of visits option is enabled, an additional call retrieves visits stored on Kameleoon Data servers.<br><br>2. Generate a visitorCode ID if no existing one is found in the KameleoonVisitorCode cookie or local storage."]
    
    %% MERGED NODE: SendEvents + its detail
    SendEvents["Send events<br>---<br>Optional: Initialize the product recommendation history."]
    
    ExecGlobalScript(["Execute the project global custom script"])
    ExecQueue(["Execute the remaining code in the Kameleoon CommandQueue"])
    EventStarted(["Send the Kameleoon::Started event"])
    
    %% MERGED NODE: WaitDOM
    WaitDOM["Wait for the DOM to be ready.<br>---<br>Optional: If the Enable support for dynamic websites option is enabled, Kameleoon listens for URL changes to reload its engine."]
    
    InitCampaigns(["Initialize campaigns and actions"])
    FlowContinues("⬇")

    %% --- MAIN FLOW CONNECTIONS ---
    ParseBrowser --> UnifySession
    UnifySession --> RetrieveData
    RetrieveData --> InitConsent
    InitConsent --> InitVisitor
    InitVisitor --> SendEvents
    SendEvents --> ExecGlobalScript
    ExecGlobalScript --> ExecQueue
    ExecQueue --> EventStarted
    EventStarted --> WaitDOM
    WaitDOM --> InitCampaigns
    InitCampaigns --> FlowContinues

    %% --- BRANCHES (Now with plenty of room to fan out) ---
    
    %% Event Branches
    PageEvent("Page event<br>---<br>Page URL and title")
    StaticDataEvent("StaticData event<br>---<br>Data about the device, OS, window size, visit number, time since previous visit, timezone, and language")
    ActivityEvent("Activity event<br>---<br>Data about clicks and activity")

    SendEvents --> PageEvent
    SendEvents --> StaticDataEvent
    SendEvents --> ActivityEvent

    %% Campaign Branches
    Adblocker("Check Adblocker<br>---<br>Check if the visitor is using an adblocker, and send an Adblocker event if they are.")
    RunMutObs("Run MutationObserver<br>---<br>Run the optimization MutationObserver to fill all classes of elements and IDs present on the page for querySelector optimization.")
    GeoOpt("Optional: Geolocation / IP<br>---<br>IP: IP request to get the user IP address (https://data.kameleoon.io/ip).<br>Geolocation: Request to get user geolocation (Country, Region, City).")
    LiveUpdateOpt("Optional: Live Update<br>---<br>Load configuration from https://SITECODE.kameleoon.com/live-experiments/config.js.")

    InitCampaigns --> Adblocker
    InitCampaigns --> RunMutObs
    InitCampaigns --> GeoOpt
    InitCampaigns --> LiveUpdateOpt

    %% --- STYLING ---
    classDef default fill:#F2F2FF,stroke:#8A8AC2,stroke-width:1px,color:#111;
    classDef whiteBox fill:#FFFFFF,stroke:#E2E2F2,stroke-width:1px,color:#111;
    classDef detailBox fill:#FFFFFF,stroke:#8A8AC2,stroke-width:2px,color:#111;
    classDef borderless fill:none,stroke:none,color:#111;

    class UnifySession,RetrieveData,PageEvent,StaticDataEvent,ActivityEvent,Adblocker,RunMutObs,GeoOpt,LiveUpdateOpt whiteBox;
    
    %% Detail boxes get a slightly thicker border to denote they contain sub-steps
    class InitVisitor,SendEvents,WaitDOM detailBox;
    class FlowContinues borderless;
```

After initialization, Kameleoon retrieves visitor data:

1. Kameleoon collects technical data such as the visitor’s browser and operating system.
2. If you enable [unify session data across subdomains](../technical-concepts/unify-session-data-storage-across-subdomains), Kameleoon retrieves past data using an iFrame on the main local storage domain.
3. If you enable `real-time sync of visits` or the [`cross-device`](../../cross-device-experimentation) option, the engine fetches usage history from Kameleoon servers.

<Note>
  Real-time sync is active by default for Kameleoon Feature Experimentation or for Safari to manage [ITP impacts](../technical-concepts/itp-management).
</Note>

4. The engine initializes a visitor. If no code exists in the `kameleoonVisitorCode` cookie or local storage, the engine assigns a new visitor code and parses all visits.
5. The engine parses and stores data such as the Page URL, device, operating system, geolocation, and goals.

<Note>
  If you use the product recommendation add-on, Kameleoon instantiates the recommendation engine here.
</Note>

6. The global custom script executes any inserted JavaScript before initializing experiments.
7. The global experiment script runs after the custom global script. This script executes regardless of targeting and stops when the experiment pauses. See [this guide](/user-manual/experimentation/web-experimentation/graphic-based-experiments/getting-started-with-the-graphic-editor#Experiment-specific_custom_script) for details.
8. The engine executes pending commands from the `CommandQueue` that do not use the `IMMEDIATE` parameter.
9. The `Kameleoon::Started` event triggers when initialization completes and the engine stores the data.
10. If you enable [SPA management](/user-manual/experimentation/web-experimentation/advanced-experiment-types/set-up-experiment-single-page-app), Kameleoon monitors URL changes. When the URL changes, the engine reloads to ensure campaigns function correctly on the new view.
11. Before initializing campaigns, Kameleoon:
    * Checks for an ad blocker.
    * Runs `mutationObserver` to optimize `querySelector` operations.
    * Fetches geolocation data from `https://eu-data.kameleoon.io/ip` if the segment requires it.
12. To load campaign configuration, Kameleoon fetches data from `https://SITECODE.kameleoon.com/live-experiments/config.js` and caches it for 2 minutes.

<Note>
  The Live Update Experiments feature refreshes experiments tagged with `LIVE-UPDATE` to allow real-time configuration changes without browser caching delays.
</Note>

### Code flow: execution

```mermaid theme={null}
graph TD
    %% Main Start Node
    StartNode(["Run all Experiments / Personalizations"])

    %% --- LEFT BRANCH: Holdout Control ---
    %% Check Node
    CheckHoldout(["Check if visitor is in an 'Holdout Control group'"])
    
    %% Outcomes
    OutcomeNo[No]
    OutcomeYes[Yes]

    %% No Flow Nodes
    HoldoutInclusionEvent(["Send holdout inclusion event"])
    IfTargetingTrue(["If targeting is 'true' for the visitor"])
    OptionalDelayedTag(["(Optional) If the campaign has a 'DELAYED' tag"])
    DownloadVariationData["<b>Download variation data</b><br>---<br>https://SITECODE.kameleoon.com/{personalizations/experiments}/$campaign_id/variations/$variation_id.js"]
    SendExpPersEvent(["Send Experiment / Personalization event"])
    
    %% Yes Flow Nodes
    HoldoutExclusionEvent(["Send holdout exclusion event"])

    %% Left Branch Connections
    StartNode --> CheckHoldout
    CheckHoldout --> OutcomeNo
    CheckHoldout --> OutcomeYes
    OutcomeNo --> HoldoutInclusionEvent
    HoldoutInclusionEvent --> IfTargetingTrue
    IfTargetingTrue --> OptionalDelayedTag
    OptionalDelayedTag --> DownloadVariationData
    DownloadVariationData --> SendExpPersEvent
    OutcomeYes --> HoldoutExclusionEvent

    %% --- RIGHT BRANCH: Targeting and Goals ---
    %% Split Point Node
    RunTargetingSegments(["Run all Targeting Segments"])
    
    %% Splitting Nodes
    %% Flow 1: Targeting Event
    IfSegmentConditions(["If the segment conditions are met for the visitor"])
    SendTargetingEvent(["Send Targeting event"])
    
    %% Flow 2: Goal Flow
    OptionalAIOpportunity(["(Optional) If the AI Opportunity Detection add-on is enabled"])
    DownloadOpportunitySegment["<b>Download opportunity segment and execute them</b><br>---<br>https://SITECODE.kameleoon.com/audiences/segments.js"]
    RunAllGoals(["Run all goals"])
    IfGoalConverted(["If the goal is converted for the visitor"])
    SendGoalEvent(["Send Goal event"])

    %% Right Branch Connections
    StartNode --> RunTargetingSegments
    
    %% Flow 1 Connections
    RunTargetingSegments --> IfSegmentConditions
    IfSegmentConditions --> SendTargetingEvent
    
    %% Flow 2 Connections
    RunTargetingSegments --> OptionalAIOpportunity
    OptionalAIOpportunity --> DownloadOpportunitySegment
    DownloadOpportunitySegment --> RunAllGoals
    RunAllGoals --> IfGoalConverted
    IfGoalConverted --> SendGoalEvent

    %% --- STYLING ---
    classDef mainStep fill:#F2F2FF,stroke:#8A8AC2,stroke-width:1px,rx:10,ry:10,color:#111;
    classDef whiteBox fill:#FFFFFF,stroke:#E2E2F2,stroke-width:1px,color:#111;
    classDef conditionalPill fill:#D0D0FF,stroke:#8A8AC2,stroke-width:1.5px,rx:10,ry:10,color:#111;
    classDef outcome fill:#ffffff,stroke:#8A8AC2,stroke-width:1px,rx:5,ry:5,color:#111;

    class StartNode,CheckHoldout,HoldoutInclusionEvent,HoldoutExclusionEvent,SendExpPersEvent,RunTargetingSegments,SendTargetingEvent,RunAllGoals,SendGoalEvent mainStep;
    class DownloadVariationData,DownloadOpportunitySegment whiteBox;
    class IfTargetingTrue,OptionalDelayedTag,IfSegmentConditions,OptionalAIOpportunity,IfGoalConverted conditionalPill;
    class OutcomeNo,OutcomeYes outcome;
```

When the experimentation loop runs, the engine:

1. If the visitor is part of a "holdout" group, the engine sends a holdout inclusion event.
2. The engine triggers an exposure event containing a `nonce`, `experimentID`, and `variationId`. For example: `eventType=experiment&nonce=5800F1BDD0667747&id=250830&variationId=978588`.
3. If the visitor is not in a holdout group, the engine sends a "Holdout exclusion event."

<Note>
  Holdout experiments allow you to manage and block other experiments:

  * The engine checks for holdout experiments first.
  * If a user is in a holdout group, the engine assigns them to a group and sends an event for reporting.
  * If the user is in the reference group, the engine blocks all other experiments.
  * If the user is in the variation group, the engine allows other experiments to proceed.

  Learn more in the [Holdout management guide](/user-manual/experimentation/feature-experimentation/create-and-manage-flags/create-and-manage-holdouts).
</Note>

The engine then starts data collection. If a personalization triggers but does not display, the engine returns one of the following blocking reasons:

* **PERSONALIZATION\_CAPPING**: The personalization reached its global visitor limit.
* **SCHEDULE**: The personalization is off based on its schedule.
* **SCENARIO**: Some scenario conditions remain unmet.
* **PRIORITY**: A higher-priority personalization exists.
* **VISITOR\_CAPPING**: The visitor reached a limit preventing display.

If you associate a `DELAYED` tag with an experiment, the engine triggers it after the first page load. The engine downloads variation data from `https://SITECODE.kameleoon.com/(personalizations)/$campaign_id/variations/$variation_id.js`.

<Note>
  Delaying an experiment is useful for popups or below-the-fold modifications where flickering is minimal.
</Note>

For the AI Opportunity Detection add-on, Kameleoon downloads segment definitions from `https://SITECODE.kameleoon.com/audiences/segments.js` to detect opportunities.

<Note>
  Kameleoon checks segments created or modified in the last 90 days, excluding those starting with `[DEV]`, `[TEST]`, or `[QA]`.
</Note>

Analyze this information on the results page to track conversion rates and potential revenue growth.
