Loading and the launch screen
Your visitor waits once, at the start. What they wait for is a choice you make, and it is the single biggest lever on how your experience feels to open.
Three ways an asset can arrive
| What happens | Good for | |
|---|---|---|
| Preloaded | fetched before the scene opens; the launch screen waits for it | anything visible in the first seconds |
| Deferred | the scene opens without it; it appears when it lands | content further into the experience |
| Streamed | played straight off the network, never fetched whole | video and audio |
Only video and audio can start playing before they have fully arrived. A model, a texture or a font still has to arrive complete before anything can use it — deferring one only means the scene opens without waiting, and the asset pops in when it is ready.
The project default, and per-asset overrides
The project setting Preload is the default for everything. Each asset can then override it:
| The asset says | Result |
|---|---|
| preload on | always waited for, whatever the project says |
| preload off | always deferred |
| inherit | follows the project |
New assets start sensibly: video and audio default to streaming — waiting for a whole video before the scene opens buys nothing — and everything else follows the project.
Two objects sharing one file can no longer disagree about how it loads. Set it once on the file.
A practical recipe
For most projects:
- Preload the first scene's models, its textures, and any font you show immediately.
- Defer everything belonging to later scenes.
- Stream all video and audio, which is already the default.
The result is a short wait followed by an experience that never blocks again.
The launch screen
Before the world exists, two screens can appear: the one the visitor taps to begin, and the one that fills up while assets arrive.
Both are configured in project settings — background, logo, the start button and its label, the onboarding prompts, the loader style.
Browsers refuse to play audio until the visitor interacts with the page. AR permissions work the same way: they are granted inside the tap that asked for them. Without a button to press, your first sound silently does not play and the camera prompt may never appear.
Replacing it with your own
The built-in screen is a preset. If you need your own, a launch script owns both screens outright — and, unusually, it also declares its own settings panel, so whoever uses it gets fields to fill in rather than code to edit.
init((ctx: PreloadContext) => {
// Declare the panel. Runs before anything is drawn — declare only, never draw.
ctx.on('describe', (p) => {
p.section('look', { en: 'Look' });
p.color('tint', { label: { en: 'Background' }, default: '#101014', section: 'look' });
p.text('caption', { label: { en: 'Caption' }, default: '', localized: true, section: 'look' });
p.boolean('hideWhenReady', { label: { en: 'Hide when ready' }, default: true });
});
ctx.on('launch', () => {
/* the screen they tap to begin */
});
ctx.on('loading', () => {
/* the screen that fills up */
});
ctx.on('progress', ({ loaded, total, percent }) => {
/* update your own bar */
});
ctx.on('ready', () => {
/* everything has arrived */
});
});
| Event | Fires when |
|---|---|
describe | the panel is being built — in the runtime and in the editor alike |
start | the runtime is starting up |
launch | show the screen the visitor taps |
loading | show the screen that fills up |
progress | assets are arriving — loaded, total, percent |
ready | everything needed has arrived |
error | something went wrong |
Parameters can be localised, so one launch script serves every language your project ships.
Whatever you use to begin must call through from the button's own click handler. Permissions — device orientation, the camera — are granted only inside the gesture that asked for them, and a start deferred to the next frame loses that right.
If it throws, the runtime drops the preset and shows the built-in screen instead.
What does not wait
Physics is not downloaded at all unless a scene needs it, and the arrangement of instanced objects and particles is generated rather than transferred. Both are covered in why this engine — they are part of the same "do not make people wait for what they do not need" idea.
Next: The shared packages