> For the complete documentation index, see [llms.txt](https://developers-apps-in-toss.toss.im/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-en/unity/build/loading-screen-customization.md).

# Customizing the loading screen

Explains how to customize the screen shown while Unity WebGL is loading.

### Loading screen files

The loading screen exists in two places.

| Path                                      | Role                                   |
| ----------------------------------------- | -------------------------------------- |
| `WebGLTemplates/AITTemplate/loading.html` | SDK default template (source)          |
| `Assets/AppsInToss/loading.html`          | Project-specific custom loading screen |

`AITPackageInitializer`is `[InitializeOnLoad]`Runs at editor startup, `Assets/AppsInToss/loading.html`If this does not exist, it copies the SDK template. Modifying this file applies the custom loading screen.

SDK template search order:

1. `Packages/im.toss.apps-in-toss-unity-sdk/WebGLTemplates/AITTemplate/loading.html`
2. `Packages/com.appsintoss.miniapp/WebGLTemplates/AITTemplate/loading.html`
3. Assembly path-based

#### Build insertion order

In the build `CopyWebGLToPublic()` step `index.html`of `%AIT_LOADING_SCREEN%` placeholder is replaced with the entire contents of the loading screen.

{% code collapsedlinecount="10" %}

```
1. Does Assets/AppsInToss/loading.html exist?
   → Yes: use the project custom loading screen
   → No: SDK default template fallback

2. If the SDK template also does not exist?
   → Debug.LogWarning("Loading screen file not found. An empty loading screen will be used.")
   → Replaced with an empty string
```

{% endcode %}

In other words, the loading screen is **inlined into index.html at build time**. Since it is not loaded as a separate file, relative path references are interpreted relative to the final `index.html` base path.

#### Revert to the default template

`AIT > Reset Loading Screen`When you run it, after a confirmation dialog it copies the SDK template `Assets/AppsInToss/loading.html`back over it. Custom content will be lost, so back it up first if needed.

#### File structure

{% code collapsedlinecount="10" %}

```
Assets/
└── AppsInToss/
    ├── Editor/
    │   └── AITConfig.asset
    └── loading.html    ← custom loading screen (applied automatically if present)
```

{% endcode %}

### App information

The app information shown on the loading screen is determined in the following order.

1. **Native app environment** (inside the Toss app) — the SDK `getAppsInTossGlobals`retrieves app information and overwrites it
2. **Fallback** (web browser, etc.) — values set in AIT Configuration are used

| Configuration                  | Description                              |
| ------------------------------ | ---------------------------------------- |
| App name (`displayName`)       | App name shown on the loading screen     |
| App icon (`iconUrl`)           | App icon URL shown on the loading screen |
| Primary color (`primaryColor`) | Progress bar color                       |

> **Note**: In the actual Toss app environment, native values take precedence, so the above settings are mainly visible in development and test environments.

### Customizable scope

`loading.html`You can freely modify the HTML, CSS, and JavaScript. `AITLoading` You can receive progress values through the API. You can represent the received values however you like.

* **UI design** — progress bars, pie charts, circular loading indicators, etc.
* **Animations** — CSS animations, JavaScript animations, GIFs, Lottie
* **Brand elements** — mascot characters, logo animations
* **Interactive elements** — mini-games, tip sliders

#### Using external resources

**StreamingAssets** (recommended) — `Assets/StreamingAssets`If placed there, it is automatically included in the build.

{% code collapsedlinecount="10" %}

```html
<img src="StreamingAssets/loading-character.gif" />
<link rel="stylesheet" href="StreamingAssets/loading-fonts.css" />
```

{% endcode %}

{% code collapsedlinecount="10" %}

```
Assets/
└── StreamingAssets/
    ├── loading-character.gif
    └── loading-fonts.css
```

{% endcode %}

**Data URI** — small images under a few KB are inlined as Base64.

{% code collapsedlinecount="10" %}

```html
<img src="data:image/png;base64,iVBORw0KGgo..." />
```

{% endcode %}

**CDN** — loads from an external URL. This introduces network dependency, and the loading screen itself may appear later.

{% code collapsedlinecount="10" %}

```html
<img src="https://your-cdn.com/loading-character.gif" />
```

{% endcode %}

### AITLoading API

`window.AITLoading`is `index.html`defined there, and the following six items are the entire public surface. `_`Members starting with \_ are internal implementation details, so do not depend on them.

| Member                 | Description                              |
| ---------------------- | ---------------------------------------- |
| `appInfo`              | `{ iconUrl, displayName, primaryColor }` |
| `onReady(callback)`    | App info ready                           |
| `onProgress(callback)` | Progress update                          |
| `onComplete(callback)` | Loading complete                         |
| `onError(callback)`    | Error occurred                           |
| `hide()`               | Hide the loading screen                  |

#### appInfo

{% code collapsedlinecount="10" %}

```javascript
console.log(AITLoading.appInfo.iconUrl);       // App icon URL
console.log(AITLoading.appInfo.displayName);   // App display name
console.log(AITLoading.appInfo.primaryColor);  // Primary color
```

{% endcode %}

The initial value is the Configuration value substituted at build time, and when native app info arrives, it is updated to that value.

#### onReady

Called when app info is ready. Use it to initialize the UI.

{% code collapsedlinecount="10" %}

```javascript
AITLoading.onReady(function(appInfo) {
    document.getElementById('app-icon').src = appInfo.iconUrl;
    document.getElementById('app-name').textContent = appInfo.displayName;
});
```

{% endcode %}

> **Important**: `onReady` The callback **should not be assumed to be called only once.** It is called once during initialization, and if native app info arrives later, it is called again with the updated `appInfo`value. Write the callback so it is safe to run multiple times (idempotent). If you register it after initialization is already complete, it is called once immediately.

#### onProgress

`0.0`from `1.0` Receives progress values between 0 and 1.

{% code collapsedlinecount="10" %}

```javascript
AITLoading.onProgress(function(progress) {
    console.log('Loading progress:', Math.round(progress * 100) + '%');
});
```

{% endcode %}

#### onComplete

Called when loading finishes. If you register it after completion, it is called immediately.

{% code collapsedlinecount="10" %}

```javascript
AITLoading.onComplete(function() {
    AITLoading.hide();
});
```

{% endcode %}

#### onError

`{ message }` Receives an object in the form of.

{% code collapsedlinecount="10" %}

```javascript
AITLoading.onError(function(error) {
    console.error('Loading failed:', error.message);
});
```

{% endcode %}

> **Note**: Failed to create WebGL context (`GLctx`, `WebGL context`, `Unable to create` series) are handled by the SDK through a dedicated path, so they do not reach this callback. Unless you want to handle cases where the device cannot open WebGL yourself, you do not need to worry about it.

#### hide

`#ait-loading-wrapper` the element `display: none`hide with.

{% code collapsedlinecount="10" %}

```javascript
AITLoading.hide();
```

{% endcode %}

### Example

Below is a hand-written example. The SDK's actual default template (dark theme) is `Assets/AppsInToss/loading.html`See it at

#### Progress bar

{% code collapsedlinecount="10" %}

```html
<style>
    /* ===== Customizable CSS variables ===== */
    :root {
        --loading-bg: #ffffff;
        --title-color: #191f28;
        --app-name-color: #333d4b;
        --progress-bg: #e5e8eb;
        --icon-size: 30px;
        --progress-height: 5px;
    }

    .loading-container {
        position: fixed;
        inset: 0;
        background: var(--loading-bg);
        display: flex;
        flex-direction: column;
        padding: 120px 20px 0;
        font-family: -apple-system, BlinkMacSystemFont, sans-serif;
    }

    .loading-title {
        font-size: 22px;
        font-weight: 600;
        color: var(--title-color);
        line-height: 1.4;
        margin-bottom: 44px;
    }

    .loading-card {
        padding: 16px;
        border: 1px solid #e5e8eb;
        border-radius: 16px;
    }

    .loading-header {
        display: flex;
        align-items: center;
        margin-bottom: 12px;
    }

    .loading-icon {
        width: var(--icon-size);
        height: var(--icon-size);
        border-radius: 8px;
        background: rgba(2, 32, 71, 0.05);
        overflow: hidden;
    }

    .loading-icon img { width: 100%; height: 100%; object-fit: cover; }

    .loading-app-name {
        margin-left: 12px;
        font-size: 15px;
        font-weight: 500;
        color: var(--app-name-color);
    }

    .loading-progress {
        height: var(--progress-height);
        background: var(--progress-bg);
        border-radius: 2.5px;
        overflow: hidden;
    }

    .loading-progress-bar {
        height: 100%;
        width: 0%;
        transition: width 0.3s ease;
    }
</style>

<div class="loading-container" id="ait-loading">
    <div class="loading-title" id="loading-title"></div>
    <div class="loading-card">
        <div class="loading-header">
            <div class="loading-icon"><img id="app-icon" src="" alt="" /></div>
            <div class="loading-app-name" id="app-name"></div>
        </div>
        <div class="loading-progress">
            <div class="loading-progress-bar" id="progress-bar"></div>
        </div>
    </div>
</div>

<script>
(function() {
    // Initialize the UI with app info (may be called again when native info arrives)
    AITLoading.onReady(function(appInfo) {
        document.getElementById('app-icon').src = appInfo.iconUrl || '';
        document.getElementById('app-name').textContent = appInfo.displayName || '';
        document.getElementById('progress-bar').style.background =
            appInfo.primaryColor || '#3182f6';
    });

    // Update progress
    AITLoading.onProgress(function(progress) {
        document.getElementById('progress-bar').style.width = (progress * 100) + '%';
    });

    // Hide the screen when loading completes
    AITLoading.onComplete(function() {
        AITLoading.hide();
    });
})();
</script>
```

{% endcode %}

#### Percentage display and error handling

{% code collapsedlinecount="10" %}

```html
<style>
    :root {
        --loading-bg: #ffffff;
        --text-color: #191f28;
        --sub-text-color: #6b7684;
    }

    .loading-container {
        position: fixed;
        inset: 0;
        background: var(--loading-bg);
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        font-family: -apple-system, BlinkMacSystemFont, sans-serif;
    }

    .loading-icon { width: 80px; height: 80px; border-radius: 20px; margin-bottom: 24px; }
    .loading-name { font-size: 18px; font-weight: 600; color: var(--text-color); }
    .loading-progress { width: 200px; height: 6px; background: #e5e8eb; border-radius: 3px; margin-top: 24px; overflow: hidden; }
    .loading-progress-bar { height: 100%; width: 0%; transition: width 0.3s ease; }
    .loading-percent { margin-top: 12px; font-size: 14px; color: var(--sub-text-color); }
</style>

<div class="loading-container" id="ait-loading">
    <img class="loading-icon" id="app-icon" alt="" />
    <div class="loading-name" id="app-name"></div>
    <div class="loading-progress"><div class="loading-progress-bar" id="progress-bar"></div></div>
    <div class="loading-percent" id="percent-text">0%</div>
</div>

<script>
(function() {
    AITLoading.onReady(function(appInfo) {
        document.getElementById('app-icon').src = appInfo.iconUrl || '';
        document.getElementById('app-name').textContent = appInfo.displayName || '';
        document.getElementById('progress-bar').style.background = appInfo.primaryColor || '#3182f6';
    });

    AITLoading.onProgress(function(progress) {
        var percent = Math.round(progress * 100);
        document.getElementById('progress-bar').style.width = percent + '%';
        document.getElementById('percent-text').textContent = percent + '%';
    });

    AITLoading.onComplete(function() {
        AITLoading.hide();
    });

    AITLoading.onError(function(error) {
        document.getElementById('percent-text').textContent = 'Loading failed';
        document.getElementById('percent-text').style.color = '#f04452';
    });
})();
</script>
```

{% endcode %}

### Troubleshooting

#### Icon not displayed

1. Check whether the icon URL is set in AIT Configuration
2. External images may be blocked by CORS policy — images from the same domain are recommended
3. In the native app environment, the app icon is loaded automatically, so fallback values may not appear

#### Custom loading screen not applied

1. The file `Assets/AppsInToss/loading.html`is in that location — other paths are not recognized
2. The loading screen is inlined at build time, so changes will not be reflected unless you rebuild after editing the file

#### Progress not updating

1. `AITLoading.onProgress()`is registered
2. Register the callback early in page load — if you register it after loading has already started, you will not receive earlier progress values

#### appInfo is empty

`AITLoading.appInfo`Instead of reading it directly `onReady` use it inside the callback. App info initialization may not have finished yet when the script runs.

### Related documents

* [Build pipeline](https://developers-apps-in-toss.toss.im/documentation/unity/build/build-process) — `%AIT_LOADING_SCREEN%` Where substitution happens
* [Build customization](https://developers-apps-in-toss.toss.im/documentation/unity/build/build-customization) — modifying the web entry point outside the loading screen
* [Get started](https://developers-apps-in-toss.toss.im/documentation/unity/first-steps/getting-started) — app info settings
* [Troubleshooting](https://developers-apps-in-toss.toss.im/documentation/unity/first-steps/faq) — across build and runtime


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-en/unity/build/loading-screen-customization.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
