> 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).

# Loading screen customization

Explains how to change the screen shown while Unity WebGL is loading, however you like.

### Loading screen file

The loading screen exists in two places.

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

`AITPackageInitializer`is `[InitializeOnLoad]`runs when the editor starts, `Assets/AppsInToss/loading.html`If this does not exist, the SDK template is copied. Modifying this file applies the custom loading screen.

SDK template lookup 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%` The placeholder is replaced with the full contents of the loading screen.

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

2. If the SDK template also doesn't exist?
   → Debug.LogWarning("Could not find the loading screen file. An empty loading screen will be used.")
   → Replaced with an empty string
```

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

#### Reverting to the default template

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

#### File structure

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

### 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`gets app information and overwrites it
2. **Fallback** (web browser, etc.) — the values set in AIT Configuration are used

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

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

### Customizable range

`loading.html`You can freely modify the HTML, CSS, and JavaScript. Get the progress from `AITLoading` API and display it however you like.

* **UI design** — progress bar, pie chart, circular loader, etc.
* **Animation** — CSS animations, JavaScript animations, GIF, Lottie
* **Brand elements** — mascot character, logo animation
* **Interactive elements** — mini games, tip sliders

#### Using external resources

**StreamingAssets** (recommended) — `Assets/StreamingAssets`If you place them there, they are automatically included in the build.

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

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

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

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

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

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

### AITLoading API

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

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

#### appInfo

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

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

#### onReady

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

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

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

#### onProgress

`0.0`from `1.0` to receive progress between.

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

#### onComplete

Called when loading is finished. If you register it after it has already completed, it is called immediately.

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

#### onError

`{ message }` object shape.

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

> **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 come to this callback. Unless you specifically want to handle cases where the device cannot open WebGL, you do not need to worry about it.

#### hide

`#ait-loading-wrapper` element `display: none`to hide it.

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

### Example

Below is an example I wrote myself. You can check the SDK's actual default template (dark theme) `Assets/AppsInToss/loading.html`here.

#### Progress bar

```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 information (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 is complete
    AITLoading.onComplete(function() {
        AITLoading.hide();
    });
})();
</script>
```

#### Percentage display and error handling

```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>
```

### Troubleshooting

#### Icon not displayed

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

#### The custom loading screen is not applied

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

#### Progress is not updating

1. `AITLoading.onProgress()`is registered
2. Callbacks should be registered early in page load — if you register after loading has already started, you cannot receive earlier progress

#### appInfo is empty

`AITLoading.appInfo`instead of reading it directly `onReady` Use it inside the callback. App information initialization may not have finished yet when the script runs.

### Related documents

* [the build pipeline](https://developers-apps-in-toss.toss.im/documentation/unity/build/build-process) — `%AIT_LOADING_SCREEN%` Where replacement happens
* [Build customization](https://developers-apps-in-toss.toss.im/documentation/unity/build/build-customization) — modify the web entry point outside the loading screen
* [Getting Started](https://developers-apps-in-toss.toss.im/documentation/unity/first-steps/getting-started) — app information 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.
