# Threlte 8 > Threlte is an open-source 3D framework for Svelte. Build interactive 3D apps for the web with declarative, reactive components powered by Three.js. These docs describe Threlte 8, the current stable major version. Threlte 7 docs are archived at https://v7.threlte.xyz. # Learn --- ## Introduction Category: Getting Started URL: https://threlte.xyz/docs/learn/getting-started/introduction If you're looking for the documentation for Threlte 7, head to [v7.threlte.xyz](https://v7.threlte.xyz). Threlte brings Three.js to Svelte in a **declarative** and **state-driven** way. It provides strictly typed components for deep **reactivity** and **interactivity** out-of-the-box. Threlte is split into distinct packages so you can import only what you need: - [@threlte/core](/docs/reference/core/getting-started) provides simple, transparent Svelte binding to Three.js: - [``](/docs/reference/core/t) is the **main building block** of any Threlte application. It is a thin, declarative wrapper for any Three.js class. - [Plugins](/docs/learn/advanced/plugins) extend behavior for `` components. - [@threlte/extras](/docs/reference/extras/getting-started) is a collection of plugins and components that add additional functionality. - [@threlte/gltf](/docs/reference/gltf/getting-started) is a command-line tool that turns GLTF assets into declarative and re-usable Threlte components. - [@threlte/rapier](/docs/reference/rapier/getting-started) provides components to enable performant physics in your Threlte application through the [Rapier engine](https://rapier.rs/). - [@threlte/theatre](/docs/reference/theatre/getting-started) provides components to enable animations in your Threlte application through the [Theatre.js animation library](https://www.theatrejs.com/). - [@threlte/xr](/docs/reference/xr/getting-started) provides components for VR and AR. - [@threlte/flex](/docs/reference/flex/getting-started) provides components to easily use the flex engine [`yoga-layout`](https://yogalayout.com/) with Threlte. --- ## App Structure Category: Basics URL: https://threlte.xyz/docs/learn/basics/app-structure Threlte uses Svelte's [Context API](https://svelte.dev/tutorial/svelte/context-api) to share `renderer`, `camera`, [and others](/docs/reference/core/use-threlte#usage) to any child component. Hooks like `useThrelte()` read from this context, so anything that needs it must be inside ``. ```svelte title="SomeComponent.svelte" ``` ## Recommended app structure Wrap your 3D app in a single `` and give it one direct child: `Scene.svelte`. Put all 3D content under that node to access Threlte hooks. ```svelte title="App.svelte" ``` ```svelte title="Scene.svelte" ``` ## Context not available The following app structure is deceiving. It looks like it should work, but **it will not**. The problem is that the `useTask` hook is called *outside* of the `` component, so the main Threlte context is not available. Usually hooks relying on some context will tell you with descriptive error messages when they are used outside of their context. ```svelte title="App.svelte" ``` ## Using a single `` In a larger Svelte app or when using SvelteKit, prefer a single `` to avoid the WebGL error “Too many active WebGL contexts. Oldest context will be lost.” We can use [Svelte 5 Snippets](https://svelte.dev/docs/svelte/snippet) to easily _portal_ our 3D content to a global ``. For that, let's first create a component that renders portal content: ```svelte title="src/lib/components/CanvasPortalTarget.svelte" {#each snippets as snippet} {@render snippet()} {/each} ``` Then implement this component in the root +layout.svelte component: ```svelte title="src/routes/+layout.svelte"
{@render children()} ``` All we need is another component that we can utilize to easily portal 3D content into the global canvas: ```svelte title="src/lib/components/CanvasPortal.svelte" ``` Now we can use this component to portal 3D content into the global canvas from anywhere in our app all while using regular DOM elements alongside our 3D content: ```svelte title="src/routes/+page.svelte" ``` --- ## Loading Assets Category: Basics URL: https://threlte.xyz/docs/learn/basics/loading-assets Threlte works seamlessly with Three.js loaders, and provides a hook, [useLoader](/docs/reference/core/use-loader), to help solve common loading issues. It caches results to prevent duplicate fetch/parse work and returns an async store, [asyncWritable](/docs/reference/core/utilities#asyncwritable) for convenient usage in Svelte. Caching cuts network requests, bandwidth, and memory, improving app performance. This section assumes you placed your assets in your public folder or in a place in your application where you can import them easily. We recommend using SvelteKit's [asset function](https://svelte.dev/docs/kit/$app-paths#asset) to help catch common loading problems. There are other equivalent Vite plugins if you're not using SvelteKit. ## Models Models come in many different formats, but `.gltf`s are usually the best fit for the web. You can use Three.js' `GLTFLoader` to load a `.gltf` model. ```svelte {#if $gltf} {/if} ``` The [``](/docs/reference/extras/gltf) component is roughly equivalent to the example above. ### `useGltf` hook `@threlte/extras` provides a handy hook for loading `.gltf` models called [useGltf](/docs/reference/extras/use-gltf): ```svelte {#await useGltf('/assets/model.gltf') then gltf} {/await} ``` ### Adjusting and reusing models You may run into the following challenges when working with models in Threlte: - Your model may have multiple parts that you'd like to adjust individually but there's no easy way to declaratively achieve that with only one `` component. - You can't seem to place multiple copies in your scene. To address both of these issues, you can use Threlte's CLI tool [@threlte/gltf](/docs/reference/gltf/getting-started) to generate a Svelte component for your model. The generated component has `` components for all of your model's parts. [Adjust the component](/docs/learn/advanced/custom-abstractions) to your liking, then import and reuse it as much as you'd like. ### Animations Three.js uses [AnimationMixer](https://threejs.org/docs/index.html#api/en/animation/AnimationMixer) to drive animations. Threlte provides a convenient [useGltfAnimations](/docs/reference/extras/use-gltf-animations) hook for gltfs. See the animation [Three.js example](https://threejs.org/examples/?q=animation) for how to setup a model for your animation needs. The Threlte docs also have an [animation example](/docs/examples/animation/animation-transitions) to help you get started. ### Common mistakes Sometimes you'll load a model and still just see a blank screen. Here are a few common mistakes that can cause this. 1. Make sure your scene has lights. 2. Make sure the model is not too small or not too big for your camera to see it. 3. Make sure your camera is looking at the model 4. Does the model render in a viewer like [gltf-viewer](https://gltf-viewer.donmccurdy.com/)? ## Textures The `TextureLoader` is another loader from Three.js that is used for textures. ```svelte {#if $texture} {/if} ``` ### `useTexture` hook `@threlte/extras` provides a handy hook for loading textures called [useTexture](/docs/reference/extras/use-texture): ```svelte {#await useTexture('/assets/texture.png') then texture} {/await} ``` ### Multiple textures Sometimes you'll want your materials to be composed of multiple textures. `useLoader` provides a way to load multiple textures at once and [spread](https://learn.svelte.dev/tutorial/spread-props) the loaded textures on a material. Loading two textures for the `map` and `normalMap` channels can be done like this: ```ts const textures = useLoader(TextureLoader).load({ map: '/assets/texture.png', normalMap: '/assets/normal.png' }) ``` or with the `useTexture` hook: ```ts const textures = useTexture({ map: '/assets/texture.png', normalMap: '/assets/normal.png' }) ``` Then spread on a material: ```svelte {#if $textures} {/if} ``` If multiple textures are given, the promise only resolves once all textures have loaded. ### Applying multiple textures to different mesh faces To declaratively apply different textures to multiple faces of a `BoxGeometry`, set the `attach` prop to a function. ```svelte { if (Array.isArray(parent.material)) { parent.material = [...parent.material, ref] } else { parent.material = [ref] } }} /> { if (Array.isArray(parent.material)) { parent.material = [...parent.material, ref] } else { parent.material = [ref] } }} /> ``` ## Other asset types Threlte provides many components to help get started with many other asset types (like [audio](/docs/reference/extras/audio)), but it doesn't have components and hooks for all of them. Checkout [Three.js examples](https://threejs.org/examples/) to see what models, techniques and effects you can achieve, then use those examples as a guide for your own [custom components](/docs/learn/advanced/custom-abstractions). ## Async loading The return value from [useLoader](/docs/reference/core/use-loader) is an `AsyncWritable` custom store. Its value will be `undefined` until the asset has loaded. Since the underlying store's value is a promise, you can use it within a Svelte `#await` block: ```svelte {#await $texture then value} {/await} ``` Assets can also be loaded after initialization by calling the `loader.load` method: ```svelte ``` The [Suspense](/docs/reference/extras/suspense) component can additionally help orchestrate loading multiple assets before displaying your scene. ## Context Awareness The `useLoader` hook, and other hooks like `useTexture`, use Svelte contexts. The assets loaded with them are only available for child components of your `` component. --- ## Handling Events Category: Basics URL: https://threlte.xyz/docs/learn/basics/handling-events Most events work the same as any other Svelte application. There's the DOM events you know from HTML, some Svelte [component props](https://svelte.dev/docs/svelte/basic-markup#Component-props) but also raycasting which is specific to 3D applications. ## DOM events The [`useThrelte`](/docs/reference/core/use-threlte) hook provides you with direct access to Threlte's wrapper div called the `dom`. The `canvas` element is also available if needed. ## Prop events The `` component has its own [events](/docs/reference/core/t#events). It can even pick up on events coming from the [underlying Three.js objects](/docs/reference/core/t#object-events). ## Raycasting events Casting rays may end up being a big part of your 3D application. They're required for creating [`interactivity`](/docs/reference/extras/interactivity) (click, pointer, mouseover etc...) on your scene's meshes. Raycasting can become expensive on complex shapes or if you have a high number of objects. This can be mitigated by either: - Raycasting against a simpler, invisible object (as is done by [mesh bounds](/docs/reference/extras/mesh-bounds)). - And/or introducing a better raycasting algorithm such as Bounding Volume Hierarchies ([BVH](/docs/reference/extras/bvh)). For simple use cases, Three.js's default raycaster works well. --- ## Scheduling Tasks Category: Basics URL: https://threlte.xyz/docs/learn/basics/scheduling-tasks In 3D apps and games, a lot of work is done in functions that run on every frame. Web-based apps rely on the browser's `requestAnimationFrame` that runs a callback function when a new frame is rendered. When encapsulating logic into smaller parts (i.e. _components_), we often need to run multiple callbacks that may be dependent on each other. For instance, we may want to update the position of an object based on user input and then render the scene with the updated position. In Threlte, these functions are called **tasks** and may or may not follow a specific order. If an order is specified, the respective task has a **dependency** to other tasks and vice versa. Tasks are grouped into **stages** and follow the same logic: They may or may not have dependencies to other stages to be executed in a specific order. A Threlte app is managed by a single **scheduler**. In this section, we will learn how to use the easy-to-use tools that the **Threlte Task Scheduling System** provides to create and orchestrate stages and tasks. Figure: A schedule of multiple stages with tasks ## Scheduler Every Threlte app has a single scheduler. It is accessible via [`useThrelte()`](/docs/reference/core/use-threlte): ```ts const { scheduler } = useThrelte() ``` Usually you won't need to interact with the scheduler directly. It is used internally by Threlte. However, you can use it to create stages and run tasks manually for more advanced use cases. ## Stages Stages are **groups of tasks**. They are executed in a specific order. ### Default Stages By default, Threlte will create two stages for you: - **`mainStage`**: This stage holds all the tasks that are not assigned to any other stage. - **`renderStage`**: This stage will be executed after the `mainStage`. It is used to render the scene and only ever executes its tasks when a re-render is needed. These two stages are created automatically and are accessible via [`useThrelte()`](/docs/reference/core/use-threlte): ```ts const { mainStage, renderStage } = useThrelte() ``` ### Creating a stage Sometimes, you may want to create your own stage, for instance to run tasks after rendering. You can do so by using the hook `useStage`. The hook will create a stage if it does not exist yet, or return the existing stage if it does. ```ts const { renderStage } = useThrelte() const afterRenderStage = useStage('after-render', { after: renderStage }) ``` All tasks added to the stage `afterRenderStage` will be executed after the tasks of the stage `renderStage`. Be aware that `useStage` never removes a stage as that's usually not needed. A stage decides **when and how its tasks are executed**. By default, a stage will execute its tasks on every frame. You can change this behavior by passing a `callback` option to `useStage`. This callback will be called every frame. The first argument `delta` is the time elapsed since the last frame. The second argument `runTasks` is a function that when invoked will run all the tasks of the stage in their respective order. You can use it to run the tasks only when needed (e.g. when a condition is met) or to run them multiple times. If a number is passed as the first argument to runTasks, the tasks will receive that as the delta. ```ts const { renderStage } = useThrelte() const conditionalStage = useStage('after-render', { after: renderStage, callback: (delta, runTasks) => { // This callback will be called every frame. The first argument is the time elapsed // since the last frame. The second argument is a function that will run all the // tasks of the stage. You can use it to run the tasks only when needed (e.g. when // a condition is met) or to run them multiple times. If a number is passed as the // first argument to runTasks, the tasks will receive that as the delta. if (condition) { runTasks() } } }) ``` ### Removing a Stage You can remove a stage by calling the `remove` method of the scheduler. The first argument is the stage or the key of the stage to remove. ```ts const { scheduler } = useThrelte() scheduler.removeStage(afterRenderStage) ``` Be aware that removing a stage will also remove all the tasks in that stage. Usually, you won't need to remove a stage. ## Tasks Tasks are functions that are executed on every frame. They are grouped in stages. You can add a task to a stage by using the hook `useTask`. The hook will create a task and add it to a stage. ### Default Tasks By default, Threlte will create a single task for you: - **`autoRenderTask`**: This task is part of [Threlte's `renderStage`](#default-stages) and will render the scene if [`autoRender`](/docs/reference/core/canvas) is set to `true`. This task is created automatically and is accessible via [`useThrelte()`](/docs/reference/core/use-threlte): ```ts const { autoRenderTask } = useThrelte() ``` ### Creating an Anonymous Task In its most basic form, `useTask` takes a function as its first argument. This function will be executed on every frame, starting on the next frame and receives the delta time representing the time since the last frame as its first argument. By default, the created task is added to [Threlte's `mainStage`](#default-stages) in an arbitrary order (i.e. without dependencies). ```ts const { task, started } = useTask((delta) => { // This function will be executed on every frame }) ``` It returns an object with the following properties: - `start`: A function that starts the task. It will be executed on the next frame. Note that by default a task is started automatically. - `stop`: A function that stops the task. It will not be executed on the next frame. - `started`: A boolean Svelte `Readable` store indicating whether the task is started or not. - `task`: The task itself. You can use it to indicate a dependency to this task on another task. ### Creating a Keyed Task You can _key_ a task by passing it as the first argument to `useTask`. This makes referencing this task easier across your app. The key can be any `string` or `symbol` value that is unique across all tasks in the stage it is added to. ```ts const { task, started } = useTask('some-task', (delta) => { // This function will be executed on every frame }) ``` ### Creating a Task in a Stage You can also pass a stage that the task should be added to as an option to `useTask`: ```ts useTask( (delta) => { // This function will be executed on every frame as a // task in the stage `afterRenderStage`. }, { stage: afterRenderStage } ) ``` ### Task Dependencies A common use case for tasks is to run code after another task has been executed. Imagine a game where an object is transformed by user input in one task and a camera follows that object in another task. The camera task should be executed after the object has been transformed. To control the order in which tasks are executed in a stage, you can pass a `before` and `after` option to `useTask`. The tasks passed to these options are called **dependencies** and can be a task itself, the key of a task or an array of tasks or keys. The referenced tasks must be in the same stage as the task you are creating. Task dependencies **do not need to be created yet** if they are passed by key. The declared dependencies will be taken into account when they are created later on. #### Examples ```ts // Execute a task after a single task passed by reference useTask( (delta) => { // … }, { after: someTask } ) ``` ```ts // Execute a task after a single task passed by key useTask( (delta) => { // … }, { after: 'some-task' } ) ``` ```ts // Execute a task after multiple tasks passed by reference useTask( (delta) => { // … }, { after: [someTask, someOtherTask] } ) ``` ```ts // Execute a task after a certain task but before another one useTask( (delta) => { // … }, { after: someTask, before: someOtherTask } ) ``` ```ts // Reference a task as a dependency that hasn't been created yet useTask( (delta) => { // If a task with the key `some-task` is created later on, // this task will be executed after it. }, { before: 'some-task' } ) useTask('some-task', (delta) => { // … }) ``` If a task is passed by reference to the `before` or `after` option, the task created by `useTask` will automatically be added to the same stage as the task it depends on. If you pass a key instead and the task you want to reference is **not** in [Threlte's `mainStage`](#default-stages), you will also need to pass the stage, either by value or key. ## Reviewing the schedule To debug the execution order, you can use the `getSchedule` method of the scheduler at any time. ```ts const { scheduler } = useThrelte() scheduler.getSchedule({ tasks: true }) ``` ```json title="Result" { "stages": [ { "key": "physics stage", "tasks": ["physics"] }, { "key": "main stage", "tasks": ["move object", "move camera"] }, { "key": "render stage", "tasks": ["render"] } ] } ``` In this example, the effective task execution order is: 1. `physics` 2. `move object` 3. `move camera` 4. `render` --- The design of the Threlte Task Scheduling System is a collaborative effort of the Threlte team, [Kris Baumgarter](https://github.com/krispya) and [Akshay Dhalwala](https://github.com/akdjr). --- ## Render Modes Category: Basics URL: https://threlte.xyz/docs/learn/basics/render-modes Threlte offers three different render modes to optimize the performance and power usage of your Threlte app. Ideally, you only want to render the scene when it is necessary, such as when the camera moves or when objects are added or removed from the scene. The render mode determines how and when the scene is rendered. In the default [`'on-demand'`](#mode-on-demand) mode, Threlte is able to determine when a re-render is necessary by observing components. When setting the render mode to [`'manual'`](#mode-manual) you must manually trigger a re-render. You can tell Threlte to continuously render the scene in the [`'always'`](#mode-always) mode. ## Mode `'on-demand'` In the mode `'on-demand'`, Threlte renders the scene only when the current frame is **invalidated**. This may happen [automatically when changes are detected](#automatic-invalidation) or the frame is [manually invalidated](#manual-invalidation). This is the default mode and the recommended way of working with Threlte. ### Automatic invalidation Threlte is able to automatically invalidate the current frame by observing component props and the mounting and unmounting of components. This means that when you e.g. change the position of a `` via component props, Threlte will automatically invalidate the current frame and request a new frame. ```svelte ``` ### Manual invalidation In some cases, you may want to manually invalidate the current frame because Threlte is not able to detect changes. To do this, you can use the `invalidate` function from the [`useThrelte`](/docs/reference/core/use-threlte) hook. ```svelte ``` ### `useTask` The [`useTask`](/docs/reference/core/use-task) hook is by default configured to automatically invalidate the current frame **on every frame**. This means that you can use it to animate your scene without having to manually invalidate the current frame. ```ts import { useTask } from '@threlte/core' useTask(() => { // useTask will automatically invalidate the current // frame, so you don't have to do it manually. }) ``` Sometimes you may want to manually invalidate the current frame from within a task. To do this, you can use the `invalidate` function from the [`useThrelte`](/docs/reference/core/use-threlte) hook and set the `autoInvalidate` option to `false`: ```ts import { useTask, useThrelte } from '@threlte/core' const { invalidate } = useThrelte() useTask( () => { // Because `autoInvalidate` is set to `false`, the current // frame will not be invalidated automatically and you can // conditionally invalidate the current frame. invalidate() }, { autoInvalidate: false } ) ``` ## Mode `'manual'` In the manual mode, you must manually trigger a re-render: ```ts const { advance } = useThrelte() advance() ``` This mode is useful when you want to have full control over when the scene is rendered. For example, you may want to render the scene only when the user interacts with the scene. ## Mode `'always'` In the `'always'` mode, Threlte continuously renders the scene. This mode is the easiest to use, but it is also the most resource intensive and should only be used when necessary. ## Setting the render mode ### `` prop You can set the render mode by setting the property `renderMode` on the [``](/docs/reference/core/canvas) component: ```svelte ``` ### `useThrelte` hook You can also set the render mode from anywhere within your Threlte app using the [`useThrelte`](/docs/reference/core/use-threlte) hook: ```ts const { renderMode } = useThrelte() renderMode.set('on-demand') ``` The renderMode property can be changed at any time, but it will only take effect on the next frame. ## Render modes and custom rendering By default, Threlte will automatically render the scene for you. In some cases, you may want to render the scene yourself, for example when using [postprocessing](/docs/examples/postprocessing/outlines). 1. Set `autoRender` to `false` on the [``](/docs/reference/core/canvas) component. This will prevent Threlte from automatically rendering the scene and you can render the scene yourself. ```svelte ``` 2. Set up a task that renders the scene. There are two ways to do this: - Add a task to [Threlte's default `renderStage`](/docs/learn/basics/scheduling-tasks#default-stages). Tasks in that stage will be executed after tasks in Threlte's `mainStage` and only when a re-render is necessary based on the current render mode. This is the recommended approach. ```ts import { useTask, useThrelte } from '@threlte/core' const { renderStage } = useThrelte() useTask( () => { // render here }, { stage: renderStage, autoInvalidate: false } ) ``` - Use `shouldRender` from the hook [`useThrelte`](/docs/reference/core/use-threlte). This function will evaluate to `true` based on the current render mode. This allows for more fine-grained control over when to render and is useful when you want to render in a task that is not in [Threlte's default `renderStage`](/docs/learn/basics/scheduling-tasks#default-stages). ```ts import { useThrelte, useTask } from '@threlte/core' const { shouldRender } = useThrelte() useTask( () => { if (shouldRender()) { // render here } }, { autoInvalidate: false } ) ``` --- ## Disposing Objects Category: Basics URL: https://threlte.xyz/docs/learn/basics/disposing-objects Freeing resources is a [manual chore in Three.js](https://threejs.org/docs/index.html#manual/en/introduction/How-to-dispose-of-objects), but Svelte is aware of component lifecycles, hence Threlte will attempt to free resources for you by calling `dispose`, if present, on all unmounted objects that are not being used anywhere else in your scene. ## Automatic disposal ```svelte ``` Be aware that calling `dispose` on a Three.js buffer, material or geometry is merely deallocating it from the GPU memory. If an object is used after it's disposed it will be allocated again, resulting in a performance drop for a single frame. It will **not produce a runtime error**. ## Manual disposal You can switch off automatic disposal by setting `dispose={false}` on components. This disables disposal for the **entire subtree**. ```svelte ``` ## Custom disposal You can use the return function of the `oncreate` prop to dispose of objects manually. ```svelte { return () => { // Do your disposal here } }} /> ``` ## Automatic Disposal Limitations Be aware that automatic disposal only happens on the objects that are referenced by a `` component. ```svelte {#if $map} {/if} ``` In this example, the texture will not be disposed when the material unmounts, you will have to dispose of it manually. --- ## Plugins Category: Advanced URL: https://threlte.xyz/docs/learn/advanced/plugins Plugins allow you to extend Threlte's [``](/docs/reference/core/t) component. They can be used to add props, event handlers, custom logic and customize the component instance. You can think of a plugin as code that is injected into every child `` component. Plugins can be overridden in child components. The [interactivity plugin in `@threlte/extras`](/docs/reference/extras/interactivity) is an example of what a plugins can do and there are a couple of other examples below. ## When to use a plugin A plugin has access to all props and the lifecycle of the `` component. Use it to: - add custom props to the `` component such as [`lookAt`](#lookat). - add custom logic to the `` component, such as automatically add helpers for certain objects in development mode. - collect object references from child components for app-wide systems such as an ECS. - build custom integrations for external libraries. [``'s `oncreate`](/docs/reference/core/t#create-event) shares some similarities to a plugin but only has access to the object referenced by the `` component. Also, it has to be defined on every `` component individually. You can think of `oncreate` as a [Svelte Attachment](https://svelte.dev/docs/svelte/@attach) for `` components. Use it for one-time setup logic that does not need access to the component's props. ## Injecting a plugin Plugins are _injected_ to a plugin context and are **accessible to all child `` components**. ```svelte title="Scene.svelte" ``` ### Plugin internals Plugins open up the component `` to external code that will be injected via context into every child instance of a `` component. The callback function receives a **reactive `args` object** that contains the `ref` of the respective `` component, all base props (`makeDefault`, `args`, `attach`, `manual` and `dispose`) and all props (anything else) passed to it. ```ts import { injectPlugin } from '@threlte/core' injectPlugin('plugin-name', (args) => { console.log(args.ref) // e.g. a Mesh console.log(args.props) // e.g. { position: [0, 10, 0] } }) ``` If a plugin decides via `args.ref` or `args.props` analysis that it doesn't need to act in the context of a certain `` component, it can return early. ```ts import { injectPlugin, isInstanceOf } from '@threlte/core' injectPlugin('raycast-plugin', (args) => { if (!isInstanceOf(args.ref, 'Object3D') || !('raycast' in args.props)) return }) ``` The code of a plugin **acts as if it would be part of the `` component itself** and has access to all properties. A plugin can run arbitrary code in lifecycle functions such as `onMount`, `onDestroy` and effects. ```ts import { injectPlugin } from '@threlte/core' import { onMount } from 'svelte' injectPlugin('plugin-name', (args) => { // Use lifecycle hooks as if it would run inside a component. // This code runs when the `` component this plugin is injected // into is mounted. onMount(() => { console.log('onMount') }) // Use any prop that is defined on the component, in this // example `count`: const count = $derived(args.props.count ?? 0) $effect(() => { // This code runs whenever count changes. console.log(count) }) return { // Claiming the property "count" so that the component // does not act on it. pluginProps: ['count'] } }) ``` A Plugin can also _claim properties_ so that the component `` does not act on it. ```ts import { injectPlugin } from '@threlte/core' injectPlugin('ecs', () => { return { // Without claiming the properties, would apply the // property to the object. pluginProps: ['entity', 'health', 'velocity', 'position'] } }) ``` Plugins are passed down by context and can be overridden to prevent the effects of a plugin for a certain tree. ```ts import { injectPlugin } from '@threlte/core' // this overrides the plugin with the name "plugin-name" for all child components. injectPlugin('plugin-name', () => {}) ``` ### Creating a plugin Plugins can also be _created_ for external consumption. This creates a _named plugin_. The name is used to identify the plugin and to override it. ```ts import { createPlugin } from '@threlte/core' export const layersPlugin = createPlugin('layers', () => { // ... Plugin Code }) ``` ```ts // somewhere else, e.g. in a component import { injectPlugin } from '@threlte/core' import { layersPlugin } from '$plugins' injectPlugin(layersPlugin) ``` ## Examples ### `lookAt` This is en example implementation that adds the property `lookAt` to all `` components, so that `` is possible: ### BVH raycast plugin A Plugin that implements [BVH raycasting](https://github.com/gkjohnson/three-mesh-bvh) on all child meshes and geometries. This plugin outlines the basic setup of the extras [bvh plugin](/docs/reference/extras/bvh). ```ts title="bvhRaycasting.svelte.ts" import { injectPlugin, isInstanceOf } from '@threlte/core' import type { BufferGeometry, Mesh } from 'three' import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from 'three-mesh-bvh' const bvhRaycasting = () => { injectPlugin('bvh-raycast', (args) => { $effect(() => { if (isInstanceOf(args.ref, 'BufferGeometry')) { args.ref.computeBoundsTree = computeBoundsTree args.ref.disposeBoundsTree = disposeBoundsTree args.ref.computeBoundsTree() } if (isInstanceOf(args.ref, 'Mesh')) { args.ref.raycast = acceleratedRaycast } return () => { if (isInstanceOf(args.ref, 'BufferGeometry')) { args.ref.disposeBoundsTree() } } }) }) } ``` Implementing this plugin in your scene looks like this. ```svelte title="Scene.svelte" ``` ## TypeScript Using TypeScript, we can achieve **end-to-end type safety for plugins**, from the plugin implementation to the props of the `` component. The example below shows how to type the props of the [`lookAt` plugin](#lookat) so that the prop `lookAt` is strictly typed on the `` component as well as in the plugin implementation. ### Typing a plugin The function `injectPlugin` accepts a type argument that you may use to type the props passed to a plugin. ```ts injectPlugin<{ lookAt?: [number, number, number] }>('lookAt', (args) => { // args.props.lookAt is now typed as [number, number, number] | undefined }) ``` ### Typing the `` component props By default, the custom props of plugins are not present on the types of the `` component. You can however extend the types of the `` component by defining the `Threlte.UserProps` type in your ambient type definitions. In a typical SvelteKit application, you can find these type definitions [in `src/app.d.ts`](https://svelte.dev/docs/kit/types#app.d.ts). ```ts title="src/app.d.ts" declare global { namespace App { // interface Error {} // interface Locals {} // interface PageData {} // interface PageState {} // interface Platform {} } namespace Threlte { interface UserProps { lookAt?: [number, number, number] } } } export {} ``` The prop `lookAt` is now available on the `` component and is typed as `[number, number, number] | undefined`. ```svelte title="Svelte.svelte" ``` As your app grows in size, you should consider moving these type these type definitions to a separate file and merge all available props to a single type definition. This type may then be used by `injectPlugin` as well as your ambient type defintions. --- ## Custom Abstractions Category: Advanced URL: https://threlte.xyz/docs/learn/advanced/custom-abstractions A lot of the components you will find in the package [@threlte/extras](/docs/reference/extras/getting-started) are abstractions on top of the [`` component](/docs/reference/core/t). These abstractions provide extra functionality like automatically invalidating the frame or providing default values or extra props. A common use case for custom abstractions is to create a component that is a fixed entity in your Threlte app which you want to reuse in multiple places or which exposes a specific behavior. As an example, let's create a component that is made up from multiple `` components resembling a tile of some kind: ```svelte title="Tile.svelte" {@render children()} ``` Let's see what implementing that component looks like: ```svelte title="Scene.svelte" ``` ## Props The `` component is now available in the scene and can be reused as many times as you want. Now we'd like to assign a different `position` to every `` in order to position it in the scene. We can do that by passing a `position` prop to the `` component: ```svelte title="Scene.svelte" ``` That doesn't work _yet_. The component `` internally needs to make use of the `position` prop to set the position of its children. We can do that by [spreading `rest` on the ``](https://svelte.dev/docs/svelte/$props#Rest-props) component at the root hierarchy of ``: ```svelte title="Tile.svelte" {5}m {@render children()} ``` ## Types The following section assumes you use TypeScript. The last thing we need to do is to add types to our custom abstraction so that IDEs like VS Code can provide autocompletion and type checking. We will create a `types.ts` file next to the `Tile.svelte` file and add the following content: ```ts title="types.ts" import type { Props } from '@threlte/core' import type { Group } from 'three' export type TileProps = Props ``` As of now it's necessary to declare the props in a separate file. If you declare them inside the ` {@render children({ ref })} ``` Now we can use the `` component in our scene and get autocompletion and type checking. ```svelte title="Scene.svelte" {#snippet children({ ref })} {/snippet} ``` --- ## WebGPU and TSL Category: Advanced URL: https://threlte.xyz/docs/learn/advanced/webgpu The WebGPU specification is still in active development. WebGPU support in Three.js is in an early stage and is subject to frequent breaking changes. As of now, we do not recommend using WebGPU in production. We highly recommend targeting [version r171](https://github.com/mrdoob/three.js/releases/tag/r171) onwards because of potential [duplication and configuration issues](https://github.com/mrdoob/three.js/pull/29404). ## WebGPU To use the WebGPU renderer, import it and then initialize it within your ``'s `createRenderer` prop. This will replace the default WebGL renderer. ```svelte title="App.svelte" {4}+ {8-14}+ { return new WebGPURenderer({ canvas, antialias: true, forceWebGL: false }) }} > ``` WebGPU is still young and has [limited availability across major browsers](https://caniuse.com/?search=webgpu). For this reason, Three.js's WebGPU renderer fallbacks to WebGL when WebGPU is not available. This same approach can be used to swap out the default renderer for any other custom renderer. The WebGPU renderer doesn't immediately render. If the renderer you provide needs to delay rendering, you can defer rendering by initially setting the renderMode to `manual`. ```svelte title="App.svelte" { const renderer = new WebGPURenderer({ canvas, antialias: true, forceWebGL: false }) renderer.init().then(() => { renderMode = 'on-demand' }) return renderer }} > ``` ### Vite WebGPU uses top-level async to determine WebGPU compatibility. Vite will often throw an error when it detects this. To circumvent this issue, the following can be added to your Vite config. ```js title=vite.config.js optimizeDeps: { esbuildOptions: { target: 'esnext' } }, build: { target: 'esnext' } ``` Alternatively, [`vite-plugin-top-level-await`](https://github.com/Menci/vite-plugin-top-level-await) can be used, although less success has been reported with this method. Adapted from [this Three.js example](https://threejs.org/examples/?q=webgpu#webgpu_performance_renderbundle). ## TSL A question that comes up often in Three.js development is "How do I extend Three.js materials?". External libraries such as [three-custom-shader-material](https://www.npmjs.com/package/three-custom-shader-material) use a find and replace solution to get this job done. Three.js has identified that it's not an ideal solution and recommends using the [Three.js Shading Language](https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language) or TSL for short. The example below is an adaptation of [this](https://threejs.org/examples/?q=tsl#webgpu_tsl_angular_slicing) Three.js example. There are many more [TSL examples](https://threejs.org/examples/?q=tsl) within Three.js that you can use or adapt for your project. ### Using the `` catalogue The `` component defaults to all the exports from `three` and will error on webgpu things like ``. This is because the `MeshPhysicalNodeMaterial` class is an export of `three/webgpu` and not `three`. Here are a few options to resolve this. #### Option 1 Extend `` with all the definitions from `three/webgpu` by using the [`extend`](/docs/reference/core/t#extending-the-default-component-catalogue) function. Adding all of the definitions will increase the bundle size of your application because both `three` and `three/webgpu` will be imported in a non-tree-shakeable way. ```svelte title="App.svelte" {3-4}+ {6}+ { return new THREE.WebGPURenderer({ canvas, antialias: true, forceWebGL: false }) }} > ``` #### Option 2 Use explicit imports for the objects, functions, and other classes that you use from `three/webgpu`. You can then use ``'s [`is`](/docs/reference/core/t#property-is) prop with those imports from `three/webgpu`. ```svelte title="Scene.svelte" {3}+ {5}+ {10}+ ``` #### Option 3 Same as option #2 but using [`extend`](/docs/reference/core/t#extending-the-default-component-catalogue) with the imports so that you can have `` etc... ```svelte title="App.svelte" {3-4}+ {6}+ { return new WebGPURenderer({ canvas, antialias: true, forceWebGL: false }) }} > ``` ```svelte title=Scene.svelte ``` Options 2 and 3 will keep the bundle size of your application small but you'll have to keep it updated as you go. #### Careful! `three` and `three/webgpu` don't mix well You will need to overwrite some of the default catalogue if you use `three/webgpu`. For example, if you're using a `MeshPhysicalNodeMaterial`, you need to update any lighing classes you use like so: ```svelte title="App.svelte" ``` ```svelte title="Scene.svelte" ``` This is because the exports from `three/webgpu` are different than those in `three` and make use of the additional features that node materials have. An easy option for projects is to start with option #1 and then transition to the other options when bundle size becomes an issue or you need to ship to production. ### Nodes The material's [nodes](https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language#nodematerial) can be directly assigned like any other prop on the `` component. ```svelte { /* ... */ })()} /> ``` Or can create the material in the script tag and use ``'s `is` prop to attach the material. ```svelte ``` Node materials give you the ability to modify three's builtin materials. In the sliced gear example, two nodes are modified; the `outputNode` and the `castShadowNode`. The `outputNode` is set up in such a way that it discards any fragments that are outside the permitted `startAngle` and `arcAngle`. If a fragment is not discarded and it is not front-facing, it is assigned the color in the `uColor` uniform. The material needs its `side` set to `THREE.DoubleSide` otherwise three.js will cull them out if they are facing away from the camera. Any fragment that is discarded in the shadowNode will not cast shadows. --- ## Migration Guides Category: More URL: https://threlte.xyz/docs/learn/advanced/migration-guides ## Threlte 8 Threlte 8 adds Svelte 5 support and removes Svelte 4 support. This upgrade contains bug fixes, better average performance, smaller bundle size, and an improved development experience. There are a few notable breaking changes listed below. ### Automatic Disposal [Automatic disposal](/docs/learn/basics/disposing-objects) has been improved to only dispose of objects that are referenced by a `` component. Objects are no longer scanned recursively for disposable objects. ```svelte {#if $map} {/if} ``` In this example, Threlte 7 also disposed of the texture when the material unmounted. This is no longer the case in Threlte 8. This change is introduced to improve performance and to make the behavior of automatic disposal more intuitive. When looking at simple examples like the one above, this might seem like a regression, but with scale the previous approach of deeply recursive automatic disposal was hard to reason about and a performance bottleneck. ### Plugin API The plugin API has been changed to allow for greater granularity and a reactivity model that is in-line with Svelte 5. #### `createPlugin` has been removed Threlte 7 included a function called `createPlugin` that allowed to separate a plugin declaration from its implementation. The recommended way to create plugins is to export a function that invokes `injectPlugin`: ```ts import { injectPlugin } from '@threlte/core' export const createSomePlugin = (pluginArg: string) => { injectPlugin('some-plugin', () => { // ... Plugin Code }) } ``` This plugin can now be implemented like this: ```svelte title="Scene.svelte" ``` #### Plugin callbacks have been removed In Threlte 7, plugins could return an object with several callback functions that were invoked when: - `ref` changed - any prop changed (e.g. `makeDefault`, `dispose`, `attach`, etc.) - any "rest" prop changed (e.g. `position`, `color`, etc.) ```ts title="Threlte 7" injectPlugin('some-plugin', ({ ref, props }) => { return { onRefChange(newRef) { // ... }, onPropsChange(props) { // ... }, onRestPropsChange(restProps) { // ... } } }) ``` These callbacks have been removed. Instead, you can use the first argument of the plugin callback, which is a reactive object containing all properties needed: ```ts title="Threlte 8" injectPlugin('some-plugin', (args) => { args.ref args.makeDefault args.args args.attach args.manual args.makeDefault args.dispose args.props // All other props declared on the component }) ``` The `args` object is reactive and will update whenever any of the referenced values change. A plugin may still return an object with `pluginProps` to specify which props the `` component should not react to. ### Events Events will no longer work, and have been replaced with callback props. ```svelte {2,3}m ``` The signature of the `oncreate` callback prop has changed. Instead of receiving an object with a cleanup function, you may now return a cleanup function that will run when the object is destroyed or its args change. This is more in-line with other apis in svelte 5 and threlte. ```svelte { return () => { console.log('cleanup') } }} > ``` The `createRawEventDispatcher` and `forwardEventHandlers` exports will no longer work. Instead of dispatching events with `createRawEventDispatcher`, invoke callback props. ```svelte ``` Instead of using `forwardEventHandlers`, pass rest props to the component you wish to forward events to. ```svelte ``` This will pass the new callback props mentioned in the previous section down the tree of components. ### Attach API & Trait Components The signature and heuristic of the attach API of the `` component has changed. The trait components `` and `` have been removed. #### `attach` Function Signature ```svelte { console.log('attaching', parent, self) return () => { console.log('detaching', parent, self) } }} /> { console.log('attaching', ref, parent, parentObject3D) return () => { console.log('detaching', ref, parent, parentObject3D) } }} /> ``` #### `attach={false}` If `false` is passed to the `attach` prop, the component will not be automatically attached to the parent object. This is useful if you want to attach the component manually. ```svelte ``` #### `attach={object3D}` If an object3D instance is passed to the `attach` prop, the component will be attached to the instance, essentially acting as a portal. Be aware that the component still acts in the given context of the parent. ```svelte ``` ### Snippets Slot props will no longer work, and must be replaced with snippets. For example, the following components from Threlte 7 would need to be migrated from this: ```svelte ``` ...to this: ```svelte {#snippet children({ ref })} {/snippet} ``` Any component that previously exposed a slot prop using the `let:` directive can follow this new pattern. ### Canvas component `size` prop The `size` property on the `` component that allowed setting specific pixel dimensions has been removed. To set a specific size of your ``, simply wrap it in an HTML element with your desired dimensions. ```svelte
``` ### Canvas component `rendererParameters` prop The `renderParameters` canvas prop has been replaced with a more powerful `createRenderer` function. If you need to manually set renderer parameters, call the function and return a renderer. ```svelte { return new WebGLRenderer({ canvas, alpha: true, powerPreference: 'high-performance', antialias: false, depth: false, premultipliedAlpha: false }) }} > ``` Any Three renderer can be returned when calling `createRenderer`. ### Transitions The transitions plugin currently does not work, we're working towards a new transition system. ### `useGltf` and `` `useGltf` and `` no longer contain a built-in `DracoLoader`, `KTX2Loader`, or `MeshoptDecoder`. Instead, separate hooks can be imported and passed to these tools, improving their bundle size and flexibility. ```ts import { useGltf, useDraco, useKtx2, useMeshopt } from '@threlte/extras' const dracoLoader = useDraco() const ktx2Loader = useKtx2() const meshoptDecoder = useMeshopt() const gltf = useGltf('./path/to/model.glb', { dracoLoader, ktx2Loader, meshoptDecoder }) ``` For more information, see the [`useGltf`](/docs/reference/extras/use-gltf) and [`GLTF`](/docs/reference/extras/gltf) docs. ### Rapier #### Two Stage Physics In order to enable fixed frame physics, the Rapier package is introducing two [scheduler stages](/docs/learn/basics/scheduling-tasks#stages). So in the most simple physics implementation: ```svelte ``` The scheduler plan will look like this: ```txt scheduler ├─ threlte-main-stage ├─ simulation │ └─ simulation ├─ synchronization │ └─ synchronization └─ threlte-render-stage ``` [Tasks](/docs/learn/basics/scheduling-tasks#tasks) that are added to the `simulation` stage will be executed according to the set [framerate](/docs/reference/rapier/framerate), i.e. the delta provided in these tasks corresponds to the delta time between physics frames. Tasks added to the `synchronization` stage will be executed after all tasks of the `simulation` stage have been executed, the delta is the regular `requestAnimationFrame` frame delta. The stages and tasks are available as part of the `RapierContext` with the `useRapier` hook: ```ts import { useTask } from '@threlte/core' import { useRapier } from '@threlte/rapier' const { simulationTask } = useRapier() useTask( () => { // E.g. interact with the physics world here }, { before: simulationStage } ) ``` #### BasicPlayerController has been removed The `BasicPlayerController` component has been removed. If you need a player controller, Rapier comes with a pre-made, easy to implement [Character Controller](https://rapier.rs/docs/user_guides/javascript/character_controller). It's more powerful and flexible than the old component. The reason is stated in rapier's documentation as well: > Despite the fact that this built-in character controller is designed to be > generic enough to serve as a good starting point for many common use-cases, > character-control (especially for the player's character itself) is often very > game-specific. Therefore the builtin character controller may not work > perfectly out-of-the-box for all game types. #### `oncreate` event signature The `oncreate` event signature available on ``, `Collider` and `` has been adapted to match the `oncreate` prop on ``. ```svelte { // ref is the created RigidBody instance return () => { // cleanup function } }} > ``` ## Threlte 7 Threlte 7 introduces a new _Task Scheduling System_ that allows you to easily orchestrate the task execution order of your Threlte application. For details on how to use it, see the [documentation](/docs/learn/basics/scheduling-tasks). Before, you had the option to choose between `useFrame` and `useRender` to orchestrate your rendering pipeline. These hooks were removed in Threlte 8. This guide will help you migrate your application to the new Task Scheduling System. This update also slightly changes the signature of the `` component as well as the Threlte context. Also, to increase performance we're enforcing the use of constant prop types on the `` component. ### Constant prop types on `` The `` component now enforces the use of _constant prop types_. This means that the type of a certain prop value must not change in the lifetime of a component. See this example: ```svelte title="Threlte 6" ``` When `changePosition` is invoked, the prop type of the prop `position` changes from an array of numbers to a number. This is not allowed anymore in Threlte 7. Prop types must be constant. It's a highly unlikely scenario that rarely occurs and a rather bad practice to start with, which allows us to optimize the performance of the `` component by enforcing this rule. This is how you would migrate the above example: ```svelte title="Threlte 7" {7}m ``` ### Threlte context ### `` props #### `frameloop` `frameloop` is now called `renderMode` as it only affects the rendering of your Threlte application. It accepts nearly the same values as before: ```ts title="Threlte 6" ``` ```ts title="Threlte 7" ``` If the value is `always`, Threlte will render your scene on every frame. If the value is `on-demand`, Threlte will only render your scene when a re-render is needed. If the value is `manual`, Threlte will never render your scene automatically and you have to trigger a re-render by calling `advance()` on the Threlte context available via `useThrelte()`. #### `autoRender` When `autoRender` is `false`, Threlte will not render your scene automatically and will enable you to implement a custom render pipeline using the hook [`useTask`](/docs/reference/core/use-task). If adding a task to render the scene to Threlte's [`renderStage`](/docs/learn/basics/scheduling-tasks#default-stages), the task will only be called in respect to the `renderMode` prop. Previously, this behavior was inferred from the usage of the `useRender` hook, but we think being explicit here is better. ### `useFrame` The hook [`useTask`](/docs/reference/core/use-task) replaces `useFrame`. It has a slightly different signature and allows you to to add a `task` to Threlte's _Task Scheduling System_. A task may have dependencies to other tasks, which you can think of as the big brother of the `order` option of `useFrame`. #### Callback Arguments The callback to `useTask` now only receives the delta time since the last frame. The Threlte context previously available as the first argument to the callback of `useFrame` should be retrieved using the hook [`useThrelte`](/docs/reference/core/use-threlte). ```ts title="Threlte 6" useFrame(({ camera, scene }, delta) => { // The Threlte context was previously available as the first // argument to the callback, followed by the delta time since the // last frame. }) ``` ```ts title="Threlte 7" const { camera, scene } = useThrelte() useTask((delta) => { // The delta time since the last frame is the only // argument to the callback. }) ``` #### `autostart` and `invalidate` The options of `useTask` have been renamed to better reflect their purpose. The `autostart` option is now called `autoStart` (note the **capital 'S'**), `invalidate` is now called `autoInvalidate`. #### If you didn't use the `order` option Replace `useFrame` with `useTask` and adapt accessing the Threlte context. ```ts title="Threlte 6" useFrame(({ camera, scene }, delta) => { // ... }) ``` ```ts title="Threlte 7" const { camera, scene } = useThrelte() useTask((delta) => { // ... }) ``` #### If you used the `order` option Migrate to `useTask` by referencing the key of the task you want to depend on. ```ts title="Threlte 6" useFrame( (_, delta) => { // This task will be executed first }, { order: 0 } ) useFrame( (_, delta) => { // This task will be executed second }, { order: 1 } ) ``` ```ts title="Threlte 7" useTask('first', (delta) => { // ... }) useTask( 'second', (delta) => { // This task will be executed after the task with the // key 'first' has been executed. }, { after: 'first' } ) ``` ### `useRender` The hook [`useTask`](/docs/reference/core/use-task) also replaces `useRender`. Previously, `useRender` allowed you to define a callback that was invoked after all `useFrame` callbacks have been invoked to render your scene with a custom render pipeline. This is now possible with `useTask` as well. Threlte provides a [`renderStage`](/docs/learn/basics/scheduling-tasks#default-stages) that only ever executes its tasks when a re-render is needed. A task added to this stage can be used to render your scene. Be sure to set the option `autoInvalidate` to `false` to prevent Threlte from automatically invalidating the render stage. ```ts title="Threlte 6" useRender(() => { // Render your scene here }) ``` ```ts title="Threlte 7" const { renderStage } = useThrelte() useTask( 'render', () => { // Render your scene here }, { stage: renderStage, autoInvalidate: false } ) ``` #### Callback Arguments The callback to `useTask` now only receives the delta time since the last frame. The Threlte context previously available as the first argument to the callback of `useRender` should be retrieved using the hook [`useThrelte`](/docs/reference/core/use-threlte). ```ts title="Threlte 6" useRender(({ camera, scene }, delta) => { // The Threlte context was previously available as the first // argument to the callback, followed by the delta time since the // last frame. }) ``` ```ts title="Threlte 7" const { camera, scene } = useThrelte() useTask((delta) => { // The delta time since the last frame is the only // argument to the callback. }) ``` #### If you didn't use the `order` option Replace `useFrame` with `useTask` and adapt accessing the Threlte context. ```ts title="Threlte 6" useRender((_{ camera, scene }_, delta) => { // ... }) ``` ```ts title="Threlte 7" const { renderStage } = useThrelte() useTask( (delta) => { // ... }, { stage: renderStage, autoInvalidate: false } ) ``` #### If you used the `order` option Migrate to `useTask` by referencing the key of the task you want to depend on. ```ts title="Threlte 6" useRender( (_, delta) => { // This task will be executed first }, { order: 0 } ) useRender( (_, delta) => { // This task will be executed second }, { order: 1 } ) ``` ```ts title="Threlte 7" const { renderStage } = useThrelte() useTask( 'first', (delta) => { // ... }, { stage: renderStage, autoInvalidate: false } ) useTask( 'second', (delta) => { // This task will be executed after the task with the // key 'first' has been executed. }, { after: 'first', stage: renderStage, autoInvalidate: false } ) ``` ## Migrating from Threlte 5 to Threlte 6 Threlte 6 provides a much more mature and feature-rich API and developer experience than its predecessor at the cost of a lot of breaking changes. This guide will help you migrate your Threlte 5 project to Threlte v6. ### Preprocessing Preprocessing is not needed anymore starting from Threlte 6. This means you may remove the preprocessor `@threlte/preprocess` from your project as well as its configuration in `svelte.config.js`. You can now use the [component ``](/docs/reference/core/t) directly. ### `` is now `` Threlte 6 merges the `` and `` components into a single component. The property `type` was renamed to `is` to also properly reflect the fact that it can be used with already instantiated objects. ### `@threlte/core` is only about the `` component The `@threlte/core` package is now only about the `` component. It does not provide any abstractions that have been part of the core package before. _Some_ of these abstractions (``, ``, audio components and several hooks) have been moved to `@threlte/extras` as this is the new home for commonly used abstractions. ### Prop types Threlte 6 heavily relies on prop types that Three.js naturally understands. As such, the prop types you may have previously used to define for example the position of an object changed. Threlte v5 provided its own prop types `Position` (e.g. `{ x, y, z }`), `Rotation` and others which are now removed or deprecated. While not yet all abstractions fully make use of the new prop types, we're working on it. Your editor should be able to provide you with the correct prop types for the components you're using. ### Interactivity Interactivity is now handled by a plugin that's available at `@threlte/extras`. It's much more mature and flexible in terms of event handling. For instance – as some of you requested – you may now define on what object the main event listener is placed. Check out [its documentation](/docs/reference/extras/interactivity) to learn more. ### `useLoader` now returns a store The hook [`useLoader`](/docs/reference/core/hooks#useloader) now returns a custom Svelte store called [`AsyncWritable`](/docs/reference/core/utilities#asyncwritable). This store allows you to [await](https://svelte.dev/tutorial/await-blocks) the loading of the resource while also implementing a regular Svelte store. It also now caches the results of the loader function so that it's not called multiple times for the same resource. You will most likely benefit from quite a performance boost in applications that rely heavily on external resources. ### `useThrelteRoot` has been removed The hook `useThrelteRoot` has been removed and its properties have partially been merged into `useThrelte` as well as a new internal context which is not exposed. All other contexts (which were used internally) have also been merged or removed. ### `` and the default effects rendering are removed In the effort of clear separation of concerns, the component `` as well as the rendering with Three.js default `EffectComposer` have been removed. Threlte 6 now provides a hook called [`useRender`](/docs/reference/core/hooks#userender) which allows you to easily set up sophisticated rendering pipelines. As soon as a `useRender` hook is implemented, Threlte's default render pipeline is disabled. `useRender` callbacks will be invoked _after_ all callback to `useFrame` have been invoked. This means that you can use `useFrame` to update your objects and `useRender` to render it. `useRender` also has the option of ordering callbacks to orchestrate the rendering pipeline across multiple components. ### Threlte's main context types Thelte's main context contains Svelte stores. These stores are now a custom Threlte store called [`CurrentWritable`](/docs/reference/core/utilities#currentwritable) which is a store that contains a `current` value with a reference to the current value of the store. This means it does not need to be unwrapped manually (and expensively!) in non-reactive places such as loops. For instance, let's have a look at its usage in the hook [`useFrame`](/docs/reference/core/hooks#useframe) where the context is available as the first argument to the callback: ```ts useFrame(({ camera, colorSpace }) => { // instead of get(camera) we now can … camera.current // THREE.Camera colorSpace.current // THREE.ColorSpace }) ``` The full type definition is [currently listed here](/docs/reference/core/hooks#usethrelte). ### `useGltfAnimations` Signature The signature of the hook `useGltfAnimations` has changed. It no longer provides a callback that is invoked when the `gltf` store has been populated and the `actions` store has been set. This is because it with the option to set a custom root for the `THREE.AnimationAction`, the callback could be triggered multiple times, leading to an unpredictable behavior. You should reside to using the `actions` store returned from the hook instead. ```ts const { actions } = useGltfAnimations(gltf) // this animation will play when the gltf store has been populated // and the actions store has been set, effectively replacing the // callback. $effect(() => { $actions.Greet?.play() }) ``` Check out the [hooks documentation](/docs/reference/extras/use-gltf-animations) for more information. ### `@threlte/rapier` #### Transform props In an effort to clearly separate concerns, the components ``, `` and `` **no longer offer transform props** (`position`, `rotation`, `scale` and `lookAt`). Instead, you should wrap these components in for instance `` components and apply transforms on these. ```svelte title="Before.svelte" {2,3}- ``` ```svelte title="After.svelte" {1-4,11}+ ``` --- ## Installation Category: Getting Started URL: https://threlte.xyz/docs/learn/getting-started/installation import ManualInstallGuide from '$components/ManualInstallGuide/ManualInstallGuide.svelte' Install only what you need. `three` and `@threlte/core` are the minimum required to get going. The remaining packages can be added anytime. [`@threlte/gltf`](/docs/reference/gltf/getting-started) doesn’t require installation, instead run it with `npx`. The components generated by `@threlte/gltf` require `@threlte/extras` to be installed. ### Choose the packages you want to use [See this comment](https://github.com/threlte/threlte/issues/8#issuecomment-1024085864) for tips on how to reduce bundle size when working with bundlers like Vite and Three.js. --- ## Your First Scene Category: Getting Started URL: https://threlte.xyz/docs/learn/getting-started/your-first-scene Understanding Svelte and Three.js is important when using Threlte. If you're new to Svelte, start with the [official tutorial](https://svelte.dev/tutorial). If you're new to Three.js, check the [official documentation](https://threejs.org) and guides. ## Structuring your app First we create a new Svelte file, `App.svelte`, where we import [``](/docs/reference/core/canvas). ```svelte title="App.svelte" ``` The `` component is the root of a Threlte app: it creates a renderer and a default camera, sets sensible defaults (like antialiasing and color management), and provides Threlte's runtime context. To access this runtime context, we'll need to create a separate component called `Scene.svelte` and include it in our `App.svelte` file. ## Creating objects At this point we're just looking at a blank screen. Let's add a simple cube to it. In `Scene.svelte`, we import the [`` component](/docs/reference/core/t), which is the **main building block** of your Threlte application. It's **a thin, generic wrapper for any Three.js class**. Here we'll create a [`THREE.Mesh`](https://threejs.org/docs/index.html#api/en/objects/Mesh) with a [`THREE.BoxGeometry`](https://threejs.org/docs/index.html#api/en/geometries/BoxGeometry) and a [`THREE.MeshBasicMaterial`](https://threejs.org/docs/index.html#api/en/materials/MeshBasicMaterial). We should now see a white cube on a transparent background. ```svelte title="Scene.svelte" {1-8}+ ``` Behind the scenes we're using the property `attach` available on `` to **attach an object to a property of its parent**. Binding geometries to the property `geometry` and materials to the property `material` is a common pattern so Threlte takes care of it for you.
Learn more We're using the property `attach` available on `` to **attach an object to a property of its parent**. In our case we're **attaching** the underlying Three.js object of `` to the property `geometry` of the `` component. We're also attaching the underlying Three.js object of `` to the property `material` of the `` component. ```svelte title="Scene.svelte" ``` Binding geometries to the property `geometry` and materials to the property `material` is a common pattern so Threlte will take care of it. It checks for the properties `isMaterial` and `isGeometry` on the underlying Three.js object and attaches it to the correct property.
Three.js equivalent ```typescript // creating the objects const geometry = new THREE.BoxGeometry() const material = new THREE.MeshBasicMaterial() const mesh = new THREE.Mesh() // "attaching" the objects mesh.geometry = geometry mesh.material = material ```
## Modifying objects That cube is still a bit boring. Let's give it some color, scale it up, and move it slightly upward. We can do this by passing props to ``. ```svelte title="Scene.svelte" {5-7}m ``` Threlte automatically generates props for `` based on the underlying Three.js object. This means you can easily guess most `` props based on the Three.js docs for the class you are using.
Three.js equivalent ```ts const mesh = new THREE.Mesh() const geometry = new THREE.BoxGeometry(1, 2, 1) const material = new THREE.MeshBasicMaterial() mesh.position.y = 1 material.color.set('hotpink') ```
The special **args** prop we use in `` corresponds to the object's constructor arguments. Props interpreted from the underlying Three.js object are called **auto props**, like `color` in our ``. Leveraging Threlte's **Pierced Props**, you can directly assign to attributes of props like `position.y` in our ``.
Learn more #### `args` In Three.js objects are classes that are instantiated. These classes can receive one-time constructor arguments (`new THREE.SphereGeometry(1, 32)`). In Threlte, constructor arguments are always passed as an array via the prop `args`. If `args` change later on, the object must naturally get reconstructed from scratch! #### Auto props For all other props, Threlte tries to automatically interpret props passed to `` component. **Step 1.** *Find Properties* - First, Threlte will try to find the **property on the underlying Three.js object** based on the **name of the prop**. In our example, [`color` is a property of `THREE.MeshBasicMaterial`](https://threejs.org/docs/index.html#api/en/materials/MeshBasicMaterial.color). **Step 2.** *Try `set` Methods* - Next, Threlte will look for a `set` method on that property and use it to set the new value. In our example it will call `material.color.set('hotpink')` to set the color of our material. **Step 3.** *Try setting the property directly* - If there's no `set` method, it will try to set the property directly. In our example, this equated to `mesh.position.y = 1`. **Step 4.** *Check for array values* - When setting a property that accepts more than one value (such as a `THREE.Vector3`: `vec3.set(1, 2, 3)`), we can pass an array as a prop. **Step 5.** *Keep the prop type constant for the lifetime of the component* - If the prop value changes, Threlte will try to set the property again. If the type of the prop value changes, Threlte won't be able to reliably do that. For instance the type of the value of a variable that is used as a prop should not change from a single number to an array of numbers. #### Pierced props Because the property `position` of our `THREE.Mesh` is a `THREE.Vector3`, it also has `x`, `y` and `z` properties which we can set directly via dot-notation, we call this **Pierced Props**.
From a performance perspective, it's often better to use pierced props because [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) prop values can safely be compared for equality. This means that if the value of a prop doesn't change, Threlte will skip any updates to the underlying Three.js object. The type of an inferred prop (or "auto prop") must be constant. This means that the type of a prop must not change for the lifetime of the component. For instance you can't use a variable as a prop that is an array of numbers and then later on change the value of that variable to a single number. This is considered a type change and therefore not allowed. ## Pointing the camera We now want to view our cube with some perspective. To manipulate the default camera, let's add the following: ```svelte title="Scene.svelte" {5-11}+ { ref.lookAt(0, 1, 0) }} /> ``` We're again using the `` component to create a [`THREE.PerspectiveCamera`](https://threejs.org/docs/index.html#api/en/cameras/PerspectiveCamera). We're also passing a `makeDefault` prop which will make this camera the default camera of our application. The renderer now uses this camera to render our scene. Threlte supports listening to certain events on `` components. Here, we use the `create` event to get a reference to the underlying Three.js object as soon as it's created and use the method `lookAt` to look at the cube. ## Enabling interactivity Let's say we want to scale our cube as soon as we hover over it. We first have to import the [`interactivity`](/docs/reference/extras/interactivity) [plugin](/docs/learn/advanced/plugins) from [`@threlte/extras`](/docs/reference/extras/getting-started) and invoke it in our `Scene.svelte` file. The `interactivity` plugin lets us add interaction event listeners like `pointerenter` and `pointerleave` to our `` components. In these event handlers we'll update the value of a `Spring` from `svelte/motion` and use its `.current` value to set the `scale` property of the `` component. ```svelte title="Scene.svelte" {3-4,6-7,20-26}+ { ref.lookAt(0, 1, 0) }} /> { scale.target = 1.5 }} onpointerleave={() => { scale.target = 1 }} > ``` You might have noticed that we're only passing a single number to the prop `scale` on ``. Threlte automatically figures out whether you are passing an array or a number and uses the appropriate underlying Three.js method.
Learn more The component `` will first look for a property `setScalar` on the underlying Three.js object and use that method if only a single number is passed. This is equivalent to calling `scale.setScalar(scale.current)`.
When working with realtime apps where variables e.g. position and rotation change constantly, an easy way observe the values is with [live expressions](https://developer.chrome.com/docs/devtools/console/live-expressions/). ## Adding animation Let's add some motion to our cube. We will use Threlte's [`useTask`](/docs/reference/core/use-task) hook to tap into Threlte's **unified frame loop** and run a function on every frame. We again use a Pierced Prop to let the cube rotate around its y-axis. ```svelte title="Scene.svelte" {2}m {10-13,25}+ { ref.lookAt(0, 1, 0) }} /> { scale.target = 1.5 }} onpointerleave={() => { scale.target = 1 }} > ``` `useTask` registers a callback that will be invoked on every frame. The callback receives the time delta since the last frame as an argument. We use the delta to update the rotation [independent of the frame rate](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) – the cube will rotate at the same speed regardless of the frame rate. ## Adjusting the lighting We're almost done. Let's add some shading to our cube and a light source. We'll use a `THREE.MeshStandardMaterial` on our cube and a `THREE.DirectionalLight` to illuminate our scene. ```svelte title="Scene.svelte" {38}m {24}+ { ref.lookAt(0, 1, 0) }} /> { scale.target = 1.5 }} onpointerleave={() => { scale.target = 1 }} > ``` ## Casting shadows We would like our cube to cast a shadow. To do so, we need a floor for it to cast a shadow _on_, so we add a new `` but this time with ``. To enable shadows, we need to set `castShadow` on both the light and our cube, and set `receiveShadow` on our new floor: ```svelte title="Scene.svelte" {26,39,45-51}+ { ref.lookAt(0, 1, 0) }} /> { scale.target = 1.5 }} onpointerleave={() => { scale.target = 1 }} castShadow > ``` ## Conclusion Congratulations, you've just created your first Three.js scene with Threlte! It includes important Three.js and Threlte concepts and should give you a good starting point for your first Threlte project. --- ## Resources Category: More URL: https://threlte.xyz/docs/learn/more/resources The Three.js community is constantly evolving and expanding. Whether you are a beginner looking to get started or an experienced developer seeking advanced knowledge, there are available resources that offer a wealth of information and technologies to help. ## Threlte's underlying libraries The following libraries are foundational to understanding Threlte's architecture, functionalities and packages: - [Three.js](https://threejs.org/) - 3D graphics on the web. Three.js serves as the backbone for rendering and creating 3D scenes in Threlte. - [Rapier](https://www.rapier.rs/) - A library for real-time physics simulations. Rapier helps bring your Threlte projects to life with dynamic interactions. - [Theatre.js](https://www.theatrejs.com/) - Focuses on timeline-based animations and offers intricate control over complex animations, making it easier to manage complex state changes in Threlte. ## General - [Three.js Resources](https://threejsresources.com/) - A hub for all things 3D. From tools to textures, lighting & more. ## Shaders GLSL (Graphics Library Shader Language) is a critical part of shader programming, which is central to achieving high-quality visual effects in Threlte. - [The Book of Shaders](https://thebookofshaders.com/) - This resource breaks down complex shader programming into bite-sized lessons. A great starting point for anyone new to shaders. - [Learn OpenGL](https://learnopengl.com/) - An in-depth resource for learning OpenGL and GLSL, covering topics from beginner to advanced levels. Learn OpenGL offers tutorials that are easily applicable to Threlte's context. We hope you find these resources valuable in your journey to mastering Threlte. Happy Learning! # Reference --- ## Getting Started Package: @threlte/flex URL: https://threlte.xyz/docs/reference/flex/getting-started Placing content and making layouts in 3D is hard. The flexbox engine [`Yoga`](https://yogalayout.com/) is a cross-platform layout engine which implements the flexbox spec. The package `@threlte/flex` provides components to easily use `Yoga` in Threlte. MatCap textures from https://github.com/emmelleppi/matcaps ## Installation ```bash npm install @threlte/flex ``` ## Usage ### Basic Example Use the component [``](/docs/reference/flex/flex) to create a flexbox container. Since there's no viewport to fill, you must specify the size of the container. Add flex items with the component [``](/docs/reference/flex/box). ```svelte ``` ### Flex Props The components `` and `` accept props to configure the flexbox. If no width or height is specified on `` components, a bounding box is used to determine the size of the flex item. The computed width or height may be different from what is specified on the `` component, depending on the flexbox configuration. To make use of the calculated dimensions of a flex item, use the slot props `width` and `height`. ```svelte {#snippet children({ width, height })} {/snippet} {#snippet children({ width, height })} {/snippet} ``` ### Nested Flex Every `` component is also a flex container. Nesting `` components allows you to create complex layouts. ```svelte {#snippet children({ width, height })} {#snippet children({ width, height })} {/snippet} {#snippet children({ width, height })} {/snippet} {/snippet} {#snippet children({ width, height })} {/snippet} ``` ### Align Flex Container The component [``](/docs/reference/extras/align) can be used to align the resulting flex container. ```svelte {#snippet children({ align })} {/snippet} ``` ### Using the Prop `class` The prop `class` can be used on `` and `` to easily configure the flexbox with predefined class names just as you would do in CSS. In order to use the prop, you need to create a `ClassParser` using the utility [`createClassParser`](/docs/reference/flex/create-class-parser) which accepts a single string and returns `NodeProps`. Let's assume, you want to create a parser that supports the following class names: ```css .container { display: flex; flex-direction: row; justify-content: center; align-items: stretch; gap: 10px; padding: 10px; } .item { width: auto; height: auto; flex: 1; } ``` You then need to create a `ClassParser` which returns the corresponding props: ```ts import { createClassParser } from '@threlte/flex' const classParser = createClassParser((string, props) => { const classNames = string.split(' ') for (const className of classNames) { switch (className) { case 'container': props.flexDirection = 'Row' props.justifyContent = 'Center' props.alignItems = 'Stretch' props.gap = 10 props.padding = 10 break case 'item': props.width = 'auto' props.height = 'auto' props.flex = 1 } } return props }) ``` Now you can use the prop `class` on `` and `` to configure the flexbox: ```svelte ``` `@threlte/flex` ships with a [default `ClassParser` which supports Tailwind-like class names](/docs/reference/flex/tailwind-parser). --- ## Getting Started Package: @threlte/xr URL: https://threlte.xyz/docs/reference/xr/getting-started The package `@threlte/xr` provides tools and abstractions to more easily create VR and AR experiences. ## Installation ```bash title="Terminal" npm install @threlte/xr ``` ## Usage ### Setup The following adds a button to start your session and controllers inside an XR manager to prepare your scene for WebXR rendering and interaction. ```svelte title="Scene.svelte" {3,11}+ ``` Then, in `scene.svelte`: ```svelte ``` This will set up your project to be able to enter a VR session with controllers and hand inputs added. If you want hands, controllers, or any other objects to be added to your `THREE.Scene` only when the XR session starts, make them children of the `` component: ```svelte ``` The ``, ``, and `` components can provide a powerful foundation when composed with other Threlte components. ### HTML HTML cannot be rendered inside an XR environment, this is just a limitation of the WebXR API. An alternative approach for creating an HTML-like UI within your XR session is to use the [threlte-uikit](https://github.com/michealparks/threlte-uikit) package. --- ## Getting Started Package: @threlte/core URL: https://threlte.xyz/docs/reference/core/getting-started The package `@threlte/core` is the core package of the Threlte framework. It provides the basic functionality of the framework, such as the `` and `` components, and hooks to interact with the Threlte state. ## Installation ```bash title="Terminal" npm install @threlte/core three @types/three ``` ## Usage Every Threlte application must be wrapped in a [`` component](/docs/reference/core/canvas). This component is responsible for creating `THREE.WebGLRenderer` and providing a state for every child component. ```svelte title="App.svelte" ``` The main building block of a Threlte application is the [`` component](/docs/reference/core/t). Use this component to instantiate any Three.js object available in the `THREE` namespace. ```svelte title="Scene.svelte" { ref.lookAt(0, 0, 0) }} /> ``` --- ## Examples Package: @threlte/flex URL: https://threlte.xyz/docs/reference/flex/examples The package `@threlte/flex` is well suited for building responsive layouts. Especially in an XR context, where using the [DOM is (currently) not possible](https://www.w3.org/TR/webxr-dom-overlays-1/) you have to rely on solutions that are native to WebGL. The following examples show how to use the `@threlte/flex` package to build responsive layouts in WebGL. The left side is the CSS implementation, the right side is using `@threlte/flex`. --- ## Package: @threlte/core URL: https://threlte.xyz/docs/reference/core/canvas The `` component is the root of your Threlte scene. It provides contexts that all other components and many hooks depend on. This means they need to be **child components** to the `` component. Check out our [guide on structuring your app](/docs/learn/basics/app-structure) for a fail-safe app architecture recipe. ## Size By default, the `` (the **DOM element** inside ``) that is being rendered into takes up 100% of the width and height of its parent element and reacts to changes in the parent element's size. This means that – simply put – you define the size of the `` element by layouting the parent element. ```svelte title="App.svelte"
``` When taking the parent's size into account, [`offsetWidth` and `offsetHeight`](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements#how_much_room_does_it_use_up) are used. ## DOM reference The context provided by the `` component contains a `dom` element. It refers to the DOM element that a particular view is rendered into. In the most common case, this is the wrapper of the `canvas` element provided by Threlte. --- ## Package: @threlte/flex URL: https://threlte.xyz/docs/reference/flex/flex The component `` is used to create the root of a flex layout. It creates the root node for [Yoga](https://yogalayout.com) and provides a context for all child [``](/docs/reference/flex/box) components to resolve their layout. ## Usage The `` component resembles the root `display: flex` container in CSS. It can be used to create a flex layout with the child components ``. Since there's no viewport to fill, you must specify the size of the container with the props `width` and `height`. ```svelte
``` ### Layout Direction Layout direction specifies the direction in which children and text in a hierarchy should be laid out. Layout direction also effects what edge `start` and `end` refer to. By default Yoga lays out with LTR layout direction. ```svelte ``` ### Layout precision Yoga uses integers for layout computation. Because in Three.js we're dealing with floating point numbers, all values are internally multiplied by a `scaleFactor` of `1000` to increase precision which is suitable for most use cases. Depending on the size of your layout, you might need to increase the `scaleFactor` to `10000` or `100000` to avoid layout issues. ```svelte ``` ### Using CSS Classes A `classParser` can be used to resolve Yoga Node Props based on classes passed to `` and `` components. This is useful if you want to use a CSS-like syntax to define your layout. You can define your own `ClassParser` using the method [`createClassParser`](/docs/reference/flex/create-class-parser) or by using a parser provided, for instance the Tailwind CSS parser [`tailwindParser`](/docs/reference/flex/tailwind-parser). ```svelte ``` ### Layout Orientation Yoga computes the layout on a 2D plane. The elements will be positioned in the 2D plane given by the two axes. By default, the layout plane is the `xy` plane. You can change the layout plane to `yz` or `xz` by setting the prop `plane`. ```svelte ``` ### Layout Reflow [Yoga](https://yogalayout.com) is a layout engine that computes the layout of a node tree. If a node (i.e. a [``](/docs/reference/flex/box) component) is added or removed from the tree, or if a node changes its properties, the layout of the component tree needs to be recomputed. This is called a **reflow**. A reflow is triggered automatically when: - `` props changes (`width`, `height`, `justifyContent`, …) - `` props changes (`justifyContent`, `flex`, …) - A `` component mounts or unmounts Because the width and height of a flex layout item may be calculated from its bounding box, the initial layout may be incorrect, for instance if a model loads into view after the initial layout has been computed. To manually request a reflow, you can use the snippet prop `reflow`: ```svelte {#snippet children({ reflow })} reflow()} /> {/snippet} ``` The `reflow` snippet prop is also available on `` components to enable encapsulated child components to easily request a reflow. You may also use the hook [`useReflow`](/docs/reference/flex/use-reflow) to request a reflow. ### Reflow Stage By default, the reflow of the layout is happening in [Threlte's mainStage](/docs/learn/basics/scheduling-tasks#default-stages). To change in what stage the layout should reflow, use the prop `reflowStage`: ```svelte ``` ### Content Dimensions Although the width and the height of the `` component are required, the **dimensions of the contents** of the `` component will be measured after a [layout reflow](#layout-reflow) and can be retrieved using the following methods: - Using the hook [`useDimensions`](/docs/reference/flex/use-dimensions) - Using the snippet props `width` and `height` ```svelte {#snippet children({ width, height })} {/snippet} ``` - Using the `reflow` event ```svelte { console.log(width, height) }} > ``` --- ## Getting Started Package: @threlte/gltf URL: https://threlte.xyz/docs/reference/gltf/getting-started A small command-line tool that turns GLTF assets into declarative and re-usable Threlte components. ## The GLTF workflow on the web is not ideal. - GLTF is thrown wholesale into the scene which prevents re-use, in Three.js objects can only be mounted once - Contents can only be found by traversal which is cumbersome and slow - Changes to queried nodes are made by mutation, which alters the source data and prevents re-use - Re-structuring content, making nodes conditional or adding/removing is cumbersome - Model compression is complex and not easily achieved - Models often have unnecessary nodes that cause extra work and matrix updates ## `@threlte/gltf` fixes that. - It creates a **virtual graph** of all objects and materials. Now you can easily alter contents and re-use. - The graph gets pruned (empty groups, unnecessary transforms, ...) for **better performance**. - It will optionally compress your model with up to **70%-90% size reduction**. ## Usage ```bash npx @threlte/gltf@latest /path/to/Model.glb [options] ``` You can also use `pnpm dlx`, `bunx`, or any other package runner. ### Options | Option | Description | | --------------------------------------------------------------------- | -------------------------------------------------------------------- | | `--output, -o` | Output file name/path | | `--types, -t` | Add Typescript definitions | | `--keepnames, -k` | Keep original names | | `--keepgroups, -K` | Keep (empty) groups, disable pruning | | `--meta, -m` | Include metadata (as `userData`) | | `--shadows, -s` | Let meshes cast and receive shadows | | `--printwidth, -w` | Prettier `printWidth` (default: 120) | | `--precision, -p` | Number of fractional digits (default: 2) | | `--draco, -d` | Draco binary path | | `--preload -P` | Add preload method to module script | | `--suspense -u` | Make the component [suspense-ready](/docs/reference/extras/suspense) | | `--isolated, -i` | Output as isolated module (no prop spreading) | | `--root, -r` | Sets directory from which .gltf file is served | | `--transform, -T` | Transform the asset for the web (draco, prune, resize) | |      `--resolution, -R` | Transform resolution for texture resizing (default: 1024) | |      `--keepmeshes, -j` | Do not join compatible meshes | |      `--keepmaterials, -M` | Do not palette join materials | |      `--format, -f` | Texture format (default: "webp") | |      `--simplify, -S` | Transform simplification (default: false, experimental) | |           `--weld` | Weld tolerance (default: 0.0001) | |           `--ratio` | Simplifier ratio (default: 0.75) | |           `--error` | Simplifier error threshold (default: 0.001) | | `--debug, -D` | Debug output | ## Example This example assumes you have your model set up and exported from an application like [Blender](https://www.blender.org/) as a GLTF file. First, run your model through `@threlte/gltf`. `npx` allows you to use npm packages without installing them. ```bash npx @threlte/gltf@latest model.glb --transform ``` This will create a `Model.svelte` file that plots out all of the assets contents. ```svelte {#await gltf} {@render fallback?.()} {:then gltf} {:catch err} {@render error?.({ error: err })} {/await} {@render children?.({ ref })} ``` Add your model to your `/static` folder as you would normally do. With the `--transform` flag it has created a compressed copy of it (in the above case `model-transformed.glb`). Without the flag just copy the original model. ```text static/ model-transformed.glb ``` The component can now be dropped into your scene. ```svelte ``` You can re-use it, it will re-use geometries and materials out of the box: ```svelte ``` Or make the model dynamic. Change its colors for example: ```svelte ``` Or exchange materials: ```svelte ``` Make contents conditional: ```svelte {#if condition} {/if} ``` ## DRACO Compression You don't need to do anything if your models are draco compressed, since `useGltf` defaults to a [draco CDN](https://www.gstatic.com/draco/v1/decoders/). By adding the `--draco` flag you can refer to [local binaries](https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/draco/gltf) which must reside in your /public folder. ## Auto-Transform With the `--transform` flag it creates a binary-packed, draco-compressed, texture-resized (1024x1024), webp compressed, deduped and pruned \*.glb ready to be consumed on a web site. It uses [glTF-Transform](https://github.com/donmccurdy/glTF-Transform). This can reduce the size of an asset by 70%-90%. It will not alter the original but create a copy and append `[modelname]-transformed.glb`. ## Type-Safety Add the `--types` flag and your component will be typesafe. ```svelte {#await gltf} {@render fallback?.()} {:then gltf} {:catch err} {@render error?.({ error: err })} {/await} {@render children?.({ ref })} ``` ## Animations If your GLTF contains animations it will add [@threlte/extras's `useGltfAnimations`](/docs/reference/extras/use-gltf-animations) hook, which extracts all clips and prepares them as actions: ```ts const gltf = useGltf('/stacy.glb') export const { actions, mixer } = useGltfAnimations(gltf, ref) ``` If you want to play an animation you can do so at any time: ```ts const onEvent = () => { $actions.jump.play() } ``` ## Skinned Meshes `useGltf` caches results by URL. This means all instances of a generated component share the same geometry, materials, skeletons, and bones. For regular meshes this is ideal — geometry and materials can be shared across instances without duplicating GPU memory. However, `SkinnedMesh` bones and skeletons are `Object3D` instances that can only have one parent at a time. If you mount multiple instances of a component that contains skinned meshes, only the last one will render correctly because it "steals" the shared bones from previous instances. `@threlte/gltf` handles this automatically. When your model contains skinned meshes, the generated component uses [`SkeletonUtils.clone`](https://threejs.org/docs/#examples/en/utils/SkeletonUtils) to clone the bone hierarchy per instance while still sharing geometry and materials from the cache: ```svelte {:then gltf} {@const clonedNodes = cloneScene(gltf.scene)} {/then} ``` This gives each component instance its own skeleton and bones, while geometry and materials remain shared — no duplicated GPU memory. Model: [RobotExpressive](https://github.com/mrdoob/three.js/tree/dev/examples/models/gltf/RobotExpressive) from the Three.js examples ## Suspense If you want to use the component [``](/docs/reference/extras/suspense) to suspend the rendering of loading components (and therefore models) and optionally show a fallback in a parent component, you can do so by passing the flag `--suspense` to make the generated Threlte component _suspense-ready_: ```svelte {#snippet fallback()} {/snippet} ``` ## Asset Pipeline In larger projects with a lot of models and assets, it's recommended to set up an asset pipeline with tools like [`npm-watch`](https://www.npmjs.com/package/npm-watch) and Node.js scripts to automatically transform models to Threlte components and copy them to the right place as this makes iterating over models and assets much faster. Here's an example script that you can use as a starting point: ```ts import { execSync } from 'node:child_process' import { existsSync, mkdirSync, readdirSync } from 'node:fs' import { join, parse, resolve } from 'node:path' /** * Transforms GLTF/GLB files into Threlte components using @threlte/gltf. * Source models are read from sourceDir, and generated Svelte components * are written directly to targetDir via the --output flag. */ const config = { sourceDir: resolve('static', 'models'), targetDir: resolve('src', 'lib', 'components', 'models'), overwrite: true, root: '/models/', types: true, keepnames: true, meta: false, shadows: false, printwidth: 120, precision: 2, draco: '', preload: false, suspense: false, isolated: true, transform: { enabled: false, resolution: 1024, format: 'webp', keepmeshes: false, keepmaterials: false, simplify: { enabled: false, weld: 0.0001, ratio: 0.75, error: 0.001 } } } mkdirSync(config.targetDir, { recursive: true }) if (!existsSync(config.sourceDir)) { throw new Error(`Source directory ${config.sourceDir} doesn't exist.`) } const gltfFiles = readdirSync(config.sourceDir).filter((file) => { return ( (file.endsWith('.glb') || file.endsWith('.gltf')) && !file.endsWith('-transformed.gltf') && !file.endsWith('-transformed.glb') ) }) if (gltfFiles.length === 0) { console.log('No gltf or glb files found.') process.exit() } for (const file of gltfFiles) { const inputPath = join(config.sourceDir, file) const outputPath = join(config.targetDir, `${parse(file).name}.svelte`) if (!config.overwrite && existsSync(outputPath)) { console.log(`${outputPath} already exists, skipping.`) continue } const args: string[] = [`--output ${outputPath}`] if (config.root) args.push(`--root ${config.root}`) if (config.types) args.push('--types') if (config.keepnames) args.push('--keepnames') if (config.meta) args.push('--meta') if (config.shadows) args.push('--shadows') args.push(`--printwidth ${config.printwidth}`) args.push(`--precision ${config.precision}`) if (config.draco) args.push(`--draco ${config.draco}`) if (config.preload) args.push('--preload') if (config.suspense) args.push('--suspense') if (config.isolated) args.push('--isolated') if (config.transform.enabled) { args.push('--transform') args.push(`--resolution ${config.transform.resolution}`) args.push(`--format ${config.transform.format}`) if (config.transform.keepmeshes) args.push('--keepmeshes') if (config.transform.keepmaterials) args.push('--keepmaterials') if (config.transform.simplify.enabled) { args.push('--simplify') args.push(`--weld ${config.transform.simplify.weld}`) args.push(`--ratio ${config.transform.simplify.ratio}`) args.push(`--error ${config.transform.simplify.error}`) } } const cmd = `npx @threlte/gltf@latest ${inputPath} ${args.join(' ')}` try { execSync(cmd, { cwd: config.sourceDir }) console.log(`Generated ${outputPath}`) } catch (error) { console.error(`Error transforming ${file}: ${error}`) } } ``` Place this script in `scripts/transform-models.ts` and run it with `npx tsx scripts/transform-models.ts`. --- ## Getting Started Package: @threlte/rapier URL: https://threlte.xyz/docs/reference/rapier/getting-started [Rapier](https://rapier.rs/) is a fast physics engine written in Rust. This package provides easy to use components and hooks to use the Rapier physics engine in Threlte. To start off, it's best to get yourself comfortable with the [basic concepts of rapier](https://rapier.rs/docs/). This package is under heavy development and its API is subject to change. Also be aware that currently only one Rapier-enabled Threlte instance is possible. ### Installation Make sure to have `@threlte/core` installed. ```bash copy npm install @threlte/rapier @dimforge/rapier3d-compat ``` --- ## Getting Started Package: @threlte/studio URL: https://threlte.xyz/docs/reference/studio/getting-started Threlte Studio is a **spatial programming toolset**. It consists of two main parts: A GUI to inspect and edit your scene and a vite plugin to sync the changes in real-time to your code. It is made to be [extendable](./authoring-extensions), so you can create your own custom components to interact with your scene and hook into the Threlte Studio API and GUI. ## Installation ```bash copy npm install @threlte/studio ``` ## Quick Start To get started, encapsulate your whole scene in the [``](./studio) component. ```svelte title=App.svelte {3,8,10}+ ``` To use auto-sync, in your vite config, insert the Threlte Studio vite plugin **before any other plugin**. ```js title=vite.config.js {2}+ {5}m import { sveltekit } from '@sveltejs/kit/vite' import { threlteStudio } from '@threlte/studio/vite' export default { plugins: [threlteStudio(), sveltekit()] } ``` ## Tips and Tricks ### Hiding from the hierarchy tree To hide items from showing in the scene hierarchy pane, add `hideInTree` to your object's userData. ```svelte ``` ### Selectable objects Scene objects are selectable by default. If you add `selectable` to your objects userData and set it to `false` that object wont be selectable. ```svelte ``` --- ## Getting Started Package: @threlte/theatre URL: https://threlte.xyz/docs/reference/theatre/getting-started [Theatre.js](https://www.theatrejs.com/) is a javascript animation library with a professional motion design toolset. It helps you create any animation, from cinematic scenes in 3D, to delightful UI interactions. ### Concepts The `@threlte/theatre` documentation cross-references [the Theatre.js documentation](https://www.theatrejs.com/docs/latest), allowing quick reference to the underlying concepts. ### Workflow Theatre.js combines programming in your IDE with editing in a browser-based GUI. The core workflow looks something like this: 1. **Create** your scene as usual, placing a `` and one or more `` in your ``. 2. **Identify** the elements and props you wish to edit in the ``, and place an `` component around them, then use the slotted components ``, `` or `` to add editable props. 3. **Edit** props and animations of elements in the `` in the browser; [config state](https://www.theatrejs.com/docs/latest/manual/projects#state) is autosaved to local storage. 4. **Export** the updated state [as a JSON file](https://www.theatrejs.com/docs/latest/manual/projects#state) by selecting your project in the studio and clicking export (top-right corner). 5. **Import** your scene's `state.json` and use it in your ``'s `config` prop. ### Installation ```bash copy npm install @threlte/theatre @theatre/core @theatre/studio ``` ### Quick Start To get started quickly, encapsulate your whole scene in the component [``](./theatre). The component `` provides a default [``](/theatre/project) and [``](/theatre/sheet) and implements [``](/theatre/studio). For a more flexible structure please consider using ``, `` and `` on their own. ```svelte title=App.svelte {3,8,10}+ ``` In your Scene, add the component `` as a parent of any component you'd wish to edit or animate. The component `` provides the components ``, `` and `` that allow you to manipulate properties in Theatre.js based on your Threlte markup. The component `` is a shortcut to add `position`, `scale` and `rotation` at once as well as mount handy `` whenever the respective Sheet Object is selected in the studio. ```svelte title=Scene.svelte {#snippet children({ Transform, Sync })} {/snippet} ``` You will now see the Theatre.js studio interface. Make yourself comfortable with the controls and if you haven't done yet, please read the Theatre.js [studio manual](https://www.theatrejs.com/docs/0.5/manual/Studio) and [keyboard shortcuts](https://www.theatrejs.com/docs/0.5/manual/keyboard-shortcuts). --- ## Package: @threlte/xr URL: https://threlte.xyz/docs/reference/xr/xr The `` component prepares your scene for a WebXR session. It sets up context that is provided by the [`useXR`](/docs/reference/xr/use-xr) hook. ## Usage ```svelte ``` The `` component will set the [``](/docs/reference/core/canvas) property `renderMode="always"` when the user enters an XR session, due to being incompatible with `on-demand` or `manual`. It will set the original value once the session has ended. Any children of the `` component will not mount until the user enters an immersive session. This is useful for adding controllers, hands, or entire scenes that should only start when the user has begun their session. ## Fallback XR sessions have to be requested actively and you might want to show contents to the user before they have entered an immersive session. You can use the `fallback` snippet to show a fallback scene to the user. ```svelte {#snippet fallback()} {/snippet} ``` --- ## Physics Framerate Package: @threlte/rapier URL: https://threlte.xyz/docs/reference/rapier/framerate import frameRate1 from './framerate-1.svg?url' import frameRate2 from './framerate-2.svg?url' Threlte's `` component has a `framerate` prop that can be used to control the framerate of the physics simulation. ## `requestAnimationFrame` Timing By default, the physics framerate is set to vary depending on the timestamp passed as the argument to the callback of `requestAnimationFrame`. Take this trivial example: ```ts const render = (time: number) => { console.log(time) requestAnimationFrame(render) } requestAnimationFrame(render) ``` Unlucky for us, the time delta between frames varies depending on several factors such as the refresh rate of the monitor and the general load of your computer or application. When using a `varying` framerate, this time delta is used to _advance the physics simulation_ and while you would think that this wouldn't make much of a difference, computers are really bad at floating point arithmetic (`console.log(0.1 + 0.2)`): Realistically no two executions of the same physics simulation will yield the same result. In short: it's **not deterministic**. For most applications, this is still the best choice since it doesn't require you to think about the aspects of handling different framerates for physics and rendering. Sometimes, however, whether you're developing a game or wish to have more control art directing the outcome, you want a physics simulation to **always yield the same result**, e.g. to be **deterministic**. In this case, you can set the `framerate` prop to a fixed number. A fixed framerate of `50` for example will _step_ the physics simulation 50 times per second with a delta of exactly `1 / 50 = 0.02` seconds – no matter the timestamp passed to `requestAnimationFrame`. The following example illustrates the difference: ## Fixed Framerate In order to efficiently use a fixed framerate, it's important to understand how Threlte is able to advance the physics simulation in a deterministic fashion while keeping the visual output smooth and in sync. First, let's have a look at the timing of the `requestAnimationFrame` API: Frame rate 1 In this example we're looking at two invokations of `requestAnimationFrame`. - **A** receives a delta of 16.35ms - **B** receives a delta of 16.1ms Threlte is making use of [the scheduler's ability](http://localhost:4321/docs/learn/basics/scheduling-tasks#creating-a-stage) to run the tasks of a stage multiple times per frame and with a fixed delta. We'd like to advance the physics simulation 200 times per second, so 5ms per step. Frame rate 2 In this example, the physics simulation is advanced 4 times as part of `requestAnimationFrame` **A**. The simulation then runs **ahead of the visual output.** After advancing the physics simulation, the visual state of the objects is rolled back from 20ms to 16.35ms while internally maintaining the physics state of after the 4th simulation step. This cycle repeats in the next frame **B**: The simulation is advanced 3 times as part of `requestAnimationFrame` **B** in order to slightly run ahead of the render time and subsequently rolled back to match the time of the render. The following example visualizes how the framerate correlates with the physics simulation running ahead of the visual output. ## `usePhysicsTask` Use the hook [`usePhysicsTask`](/docs/reference/rapier/use-physics-task) to interact with the physics simulation. It's a wrapper around the [`useTask` hook](/docs/reference/core/use-task) that automatically adds the handler to the `simulation` stage and always runs _before_ the physics world is stepped. --- ## Package: @threlte/core URL: https://threlte.xyz/docs/reference/core/t The component `` provides the means to use **any Three.js export** as a Svelte component. It does this by leveraging the rigid Three.js naming and object property structure to add and remove objects to and from the scene graph, attach objects to parent object properties or add event listeners. `` is the main building block of any Threlte application. Components available in `'@threlte/extras'` are built on top of `` and may provide a more convenient API for specific Three.js classes. ## Usage types
Primer on terminology ```ts // Class definition: class Mesh extends THREE.Object3D { constructor(geometry, material) { /* … */ } } // Creating a class instance: const mesh = new Mesh() // Creating a class instance with constructor arguments: const mesh = new Mesh(geometry, material) ```
There are two ways to use ``: - Use dot-notation to use any Three.js class imported from `'three'`: ```svelte ``` - Pass the property `is` to ``: ```svelte ``` Both ways are **equivalent and can be used interchangeably**. The latter is more explicit and allows you to use any class definition (even if it's not a `'three'` import), object instance or virtually any other value. The next section will discuss both ways in more detail. ### Dot notation Any Three.js class imported from `'three'` can be used as a component with full type-safety. The name of the import is the same as the name of the component. For example, the class `THREE.Mesh` can be used with the component ``. Let's take a look at a simple example: ```svelte ``` Let's break this down: - The component `` creates an instance of [`THREE.Mesh`](https://threejs.org/docs/index.html?q=mesh#api/en/objects/Mesh) which is automatically added to the scene graph. - The component `` creates an instance of `THREE.BoxGeometry` which is automatically ["attached"](#attach) to the property `geometry` of the parent `THREE.Mesh`. - The component `` creates an instance of `THREE.MeshBasicMaterial` which is automatically ["attached"](#attach) to the property `material` of the parent `THREE.Mesh`. #### Extend the default component catalogue If you want to use a class that is not a `'three'` import with Threlte's dot-notation, you can extend the default component catalogue. Be aware that components used this way do not offer type-safety. Once extended, the updated catalogue is available to all components in your application. ```svelte title="Camera.svelte" {5-7} {#snippet children({ ref })} {/snippet} ``` ### Property `is` To explicitly pass a class definition to the component ``, use the property `is`. Let's take a look at the same example as above but using the property `is`: ```svelte ``` The two examples are **equivalent** and can be used interchangeably. The "vanilla" Three.js equivalent of both examples would be: ```ts import { Mesh, BoxGeometry, MeshBasicMaterial } from 'three' const mesh = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshBasicMaterial()) scene.add(mesh) ``` Using the property `is` comes in handy when using classes that are not exported from Three.js' main namespace `'three'`, such as the `OrbitControls` class: ```svelte ``` #### What's happening under the hood? If a **class definition** such as `THREE.Mesh` is provided to the property `is`, it creates an instance of that class which we call `ref` – the component's reference to the Three.js object: ```svelte ``` If a **class instance** (such as `new THREE.Mesh()`) or **any other value** is provided, the component uses this value as-is: ```svelte ``` Depending on the `is` property value types, Threlte makes certain assumptions: - If the value passed to `is` is extending `THREE.Object3D` it's added to the scene graph. - If the value passed to `is` is a disposable, it's disposed when the component unmounts or whenever the `args` change and a new `ref` is created. - If the value passed to `is` is has a property `addEventListener`, you can add [event callbacks](#events). - If the value passed to `is` is extending `THREE.Camera`, certain [camera-related properties](#camera-props) are available. ## Props The `` component has a set of fixed props (namely `args`, `is`, `attach`, `manual`, `makeDefault` and `dispose`) that are used to set up the Three.js object. On top of that, you can use arbitrary props to reactively set any property of the underlying Three.js object. ### Three.js object props To understand how it works, let's have a look at a simple example. Let's say we want to render a simple cube. We can do this by using the `` component: ```svelte ``` Using automatic [**attach**](/docs/reference/core/t#attach), the geometry as well as the material are assigned to the mesh. What if we want to change the color of the material to `"red"`? We can do this by using the `color` prop: ```svelte {7}m ``` Keep in mind that this property is not _hard-wired_. We can use any property of the underlying Three.js object as a prop on the `` component. For example, we can also set the `position` property of the `THREE.Mesh`: ```svelte {5}m ``` Because the property `position` of a `THREE.Mesh` is a `THREE.Vector3` the value we have to provide is what is passed to the [`set` function](https://threejs.org/docs/index.html?q=mesh#api/en/math/Vector3.set) of the `THREE.Vector3` class. In this case, we pass an array of three numbers (`[x, y, z]`). Using an editor like VS Code, you benefit from type hints and auto-completion. We only changed the `y` coordinate of the position, so we can use a **pierced prop** to only change the `y` coordinate: ```svelte {5}m ``` This way, we get less updates of the underlying Three.js object because the value of the prop is a primitive value which can easily be compared to the previous value. Unfortunately with pierced props we miss out on the type hints and auto-completion. The type of an inferred prop (or "auto prop") must be constant. This means that the type of a prop must not change for the lifetime of the component. For instance you can't use a variable as a prop that is an array of numbers and then later on change the value of that variable to a single number. This is considered a type change and therefore not allowed. ### args Three.js objects are **class instances**. When Instantiating these objects, they can receive one-time constructor arguments (`new THREE.SphereGeometry(1, 32)`). In Threlte, constructor arguments are always passed as an array via the property `args`. Changing `args` later on should be avoided as that will reconstruct the class instance. - If a **class definition** such as `THREE.BoxGeometry` is provided to the property `is`, the property `args` is used to instantiate the class: `` equals `new BoxGeometry(1, 2, 1)`. ### attach Use `attach` to _attach_ objects to other objects. The following attaches a material to the material property of a mesh and a geometry to the geometry property: ```svelte ``` All materials receive `attach="material"`, and all geometries receive `attach="geometry"` automatically. You do not strictly have to type it out! - The object referenced by the `` component is "attached" to the parent object's property. ```svelte ``` - `attach` can be a dot-notated path to a nested parent property: ```svelte ``` - `attach` can also be a function which is called when the component is created. This function receives the parent, the closest upstream parent `THREE.Object3D` and the value inferred from the property `is` as arguments. It can return a function which is called whenever the component is destroyed or the `args` change and a new `ref` is created: ```svelte { console.log('attaching', ref, parent, parentObject3D) parent.shadow.camera = ref return () => { parent.shadow.camera = null } }} /> ``` - You may also pass an object3D instance to the `attach` prop. This allows you to attach the object to a specific parent object, essentially acting as a portal. ```svelte ``` - To disable attaching, pass `false`. This is useful if you want to attach the object manually: ```svelte ``` ### Camera props By default Threlte is responsive and will update camera properties (aspect ratio, etc.) on resize. Cameras can be controlled manually by setting `manual` to `true`. This will opt out of projection matrix recalculation when the drawing area resizes or other camera-related properties change. ```svelte ``` Use the property `makeDefault` to set a camera to the default rendering camera. ```svelte ``` A common mistake is to forget setting `makeDefault`. If you do not set a camera to be the default camera, the scene will not be rendered through this camera but through Threlte's default camera. ## Events ### Object events Adding an event listener to a component will also add the corresponding event listener to the Three.js class instance. The event will be forwarded and the native payload is available as the first argument to the event listener. This will listen to the "change" event on the `THREE.OrbitControls`: ```svelte console.log('change:', e)} /> ``` ### Create event All `` components also emit the `create` event when the underlying Three.js class instance is created. This can be used to access the instance from the parent component or do tasks on the objects upon creation. The event handler is called with a reference to the object. You may return a cleanup callback. It will be invoked when the component unmounts or when the object is re-instantiated. ```svelte { // Look at the center ref.lookAt(0, 0, 0) return () => { // Do something when the camera is disposed } }} /> ``` ### Interaction events By default, `` doesn't have any click, pointer or wheel events; however, pointer events can be enabled using the [`interactivity`](/docs/reference/extras/interactivity) plugin. ## Snippet props The object referenced by the component is available as the snippet prop `ref`: ```svelte {#snippet children({ ref: camera })} {/snippet} ``` ## Bindings The object referenced by the component is available as the binding `ref`: ```svelte ``` ## Extending the default component catalogue By default when using the dot-notation to access Three.js objects (e.g. ``), Threlte will automatically import the corresponding class from the namespance `'three'`. If you want to use a custom class or classes from Three.js that are available elsewhere (like `OrbitControls`), you can **extend the default catalogue** with the `extend` function: ```svelte ``` ### Custom component catalogue types By default, TypeScript will not pick up custom custom items added to the catalogue by using the `extend` function. To extend the default catalogue types, define the `Threlte.UserCatalogue` type in your ambient type definitions. In a typical SvelteKit application, you can find these [in `src/app.d.ts`](https://svelte.dev/docs/kit/types#app.d.ts). ```ts title="src/app.d.ts" import type { UserCatalogue } from '@threlte/core' declare global { namespace App { // interface Error {} // interface Locals {} // interface PageData {} // interface PageState {} // interface Platform {} } namespace Threlte { interface UserCatalogue { CustomMesh: typeof CustomMesh } } } export {} ``` ## Plugins The component `` can be extended in functionality with [Threlte Plugins](/docs/reference/core/plugins). ### Custom prop types Plugins may add custom props to the `` component. By default, these props are not picked up by TypeScript. To extend the types of the `` component and define custom prop types, define the `Threlte.UserProps` type in your ambient type definitions. In a typical SvelteKit application, you can find these type definitions [in `src/app.d.ts`](https://svelte.dev/docs/kit/types#app.d.ts). ```ts title="src/app.d.ts" declare global { namespace App { // interface Error {} // interface Locals {} // interface PageData {} // interface PageState {} // interface Platform {} } namespace Threlte { interface UserProps { myProp?: string } } } export {} ``` ```svelte ``` --- ## Package: @threlte/flex URL: https://threlte.xyz/docs/reference/flex/box The component `` creates a flexbox item. It can be used as a direct child of [``](/docs/reference/flex/flex) or as a child of another `` to create a nested flex layout. ## Usage ```svelte {#snippet children({ width })} {/snippet} ``` ### Content Sizing The `` component controls element positions only. However, if you wish to handle element dimensions based on the layout calculated by Yoga, you'll need to manually adapt the content's size. This is because `@threlte/flex` lacks knowledge about the inner content's sizing mechanisms. For this purpose, `@threlte/flex` offers the _computed dimensions_ in three ways: - Using the `width` and `height` snippet props ```svelte {#snippet children({ width, height })} {/snippet} ``` - Using the [`useDimensions`](/docs/reference/flex/use-dimensions) hook in a child component to ``: ```svelte title="Child.svelte" ``` - Using the `reflow` event ```svelte { console.log(width, height) }} > ``` ### Layout Reflow To trigger a [layout reflow](/docs/reference/flex/flex#layout-reflow), you can use the `reflow` slot prop: ```svelte {#snippet children({ reflow })} {/snippet} ``` ### Item Ordering By default, the order of a flex item is determined by the order of insertion in the component tree. If for any reason you need to change the order of a flex item manually, you can use the `order` prop: ```svelte ``` --- ## Package: @threlte/rapier URL: https://threlte.xyz/docs/reference/rapier/world This component provides the basic physics context and loads [rapier](https://rapier.rs/). All components that rely on physics (e.g. `` or ``) must be a child of ``. ## Structure A typical structure of a physics-enabled wrapper component might look like this: ```svelte title="Wrapper.svelte" ``` This structure ensures that all components inside the component `` have access to the physics context. ## Fallback [rapier](https://rapier.rs/) is a Rust-based physics engine and as such bundled and used as a WASM module. If loading of rapier fails for any reason, a slot with the name `fallback` is mounted to e.g. display a fallback scene without physics. ```svelte title="Wrapper.svelte" {#snippet fallback()} {/snippet} ``` --- ## Deploying To Production Package: @threlte/studio URL: https://threlte.xyz/docs/reference/studio/deploying-to-production ## Common Pattern Typically you would want to remove the Studio from your app in production. This can be done by wrapping the Studio component in a conditional statement that checks if the app is in development mode: ```svelte title=App.svelte {#if import.meta.env.MODE === 'development'} {#await import('@threlte/studio') then { Studio }} {/await} {:else} {/if} ``` This way, the Studio will only be included in your app when it is in [development mode](https://vitejs.dev/guide/env-and-mode.html#modes). ### Vite Plugin The Threlte Studio vite plugin is only enabled in development mode. --- ## Package: @threlte/theatre URL: https://threlte.xyz/docs/reference/theatre/theatre The component `` is a convenience shortcut and provides a default `` and `` to get you set up as fast as possible. It also includes a `` which can be disabled with the property `studio`: `` ### Example The component `` is a good choice if you want to test the waters or to quickly spin up an experiment. ```svelte {#snippet children({ Transform })} {/snippet} {#snippet children({ Transform })} {/snippet} ``` --- ## Package: @threlte/theatre URL: https://threlte.xyz/docs/reference/theatre/project Theatre.js work is organized into projects that group animation [\](./sheet)s. Projects also provide the means to inject configuration state exported as a JSON file from the [\](./studio) back into your code through a prop: ``. While multiple projects may be created, one is usually sufficient for a whole Threlte application. #### Theatre.js Docs **Project** | [Project Manual](https://www.theatrejs.com/docs/latest/manual/projects) | [Project API Reference](https://www.theatrejs.com/docs/latest/api/core#project) ### Creating a Project ```svelte ``` ### Loading a Saved State The state of a project edited in the [\](./studio) is saved in your browser's local storage, and can be [exported from within the studio interface](https://www.theatrejs.com/docs/latest/manual/projects#state). It's a JSON file containing all animated and static properties of all sheets of the project. ```svelte ``` --- ## Package: @threlte/theatre URL: https://threlte.xyz/docs/reference/theatre/sheet Theatre.js sheets contain one or more Theatre.js objects and optionally a [``](/docs/reference/theatre/sequence) component that allows controlling the animation. The animated objects can adjusted in the [``](/docs/reference/theatre/studio). #### Theatre.js Docs **Sheet** | [Sheet Manual](https://www.theatrejs.com/docs/latest/manual/sheets) | [Sheet API Reference](https://www.theatrejs.com/docs/0.5/api/core#sheet) ## Creating Sheets You can create a sheet by placing the component `` as a child of a [``](/theatre/project) component. If a sheet with the given name already exists, it will represent the existing sheet instead of creating a new one. ```svelte ``` ## Playing a Sheet's animation Each Theatre.js sheet has a sequence attached to it. The sequence is the heart of the Theatre.js API: it determines where we are in the animation timeline, and provides and API to play and pause the animation in a variety of ways. #### Using the `` component The first way to control the sequence is using a reactive API with the [``](/docs/reference/theatre/sequence) component. #### Using the `useSequence` hook xxx --- ## Package: @threlte/theatre URL: https://threlte.xyz/docs/reference/theatre/sequence Sequences are the heart of the Theatre.js animation system. The sequence represents the animation timeline and provides an API for controlling its playback. You can reactively control animations through the `` component, which you place inside a [``](/docs/reference/theatre/sheet). Currently, you can only have one sequence in each sheet. Future versions of Theatre.js are expected to support multisequence sheets. #### Theatre.js Docs **Sequence** | [Sequence Manual](https://www.theatrejs.com/docs/latest/manual/sequences) | [Sequence API Reference](https://www.theatrejs.com/docs/0.5/api/core#sequence) ## Usage The following example shows how `` can be used to build a simple playback controller. ## Lifecycle Threlte provides lifecycle props to allow you to configure how the sequence playback is connected to the Svelte component lifecycle. See the `autoplay`, `autoreset` and `autopause` props below. Note that the underlying Theatre.js sheets are persisted even when unmounting a `` component. That's why the sequence doesn't reset automatically when unmounting a ``, and why the `autoreset` options is required. ## Audio The audio options allow you to attach a soundtrack to your animation sequence. Theatre.js achieves this using the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API). For more details, see [audio manual](https://www.theatrejs.com/docs/0.5/manual/audio) and [attach audio API reference](https://www.theatrejs.com/docs/0.5/api/core#sequence.attachaudio_opts_) ## Snippet Prop When using the sequence in a child component, a snippet prop can come in handy. ```svelte {#snippet children({ play })} {#snippet children({ Transform })} {/snippet} {/snippet} ``` --- ## Package: @threlte/theatre URL: https://threlte.xyz/docs/reference/theatre/studio The `` component enables the Theatre.js studio interface in your browser. It is intended for use in development. See the Theatre.js docs for extended instructions for using the studio interface. #### Theatre.js Docs **Studio** | [Studio Manual](https://www.theatrejs.com/docs/latest/manual/Studio) | [Studio keyboard Shortcuts](https://www.theatrejs.com/docs/latest/manual/keyboard-shortcuts) | [Studio API Reference](https://www.theatrejs.com/docs/latest/api/studio) ### Example In most cases, you want the interface while editing animations or laying out scenes. While other parts of Theatre.js are performant and built for production, `@theatre/studio` is currently not, and shouldn't be included in your production bundle. ```svelte ``` ### Exporting State When editing your project in the studio, state is automatically saved to your browser's local storage. To export the state, select your project from the outline panel (top-left) and click the export in the details panel (top-right). For more information and a video, see [the Theatre.js state docs](https://www.theatrejs.com/docs/latest/manual/projects#state). --- ## Package: @threlte/xr URL: https://threlte.xyz/docs/reference/xr/button-vr `` is an HTML ` {/snippet} ``` ## uikit An alternative to using HTML for UI is [uikit](https://pmndrs.github.io/uikit/docs/getting-started/vanilla). The vanilla code has be wrapped into [threlte-uikit](https://github.com/threlte/threlte-uikit) for use in Threlte projects. There are situations where this package is necessary, for instance the `` component cannot be used within XR sessions. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/hud Renders a heads-up-display (HUD). Each HUD creates a new scene rendered on top of the main scene with a separate [Threlte context](/docs/reference/core/use-threlte) and camera. The HUD component creates a partially new Threlte context, specifically a new scene and camera. Everything else in `useThrelte` is preserved and reused. Because creating a `` is somewhat similar to creating a ``, it is recommended to use the same best practices and place all objects you want in the HUD within a new `Scene` component: ```svelte title=MyHUD.svelte {2,6}+ ``` ```svelte title=Scene.svelte ref.lookAt(0, 0, 0)} /> ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/image-material Adapted from drei's [``](https://github.com/pmndrs/drei?tab=readme-ov-file#image) component, with additional color processing extras. A shader-based image material component with auto-cover (similar to css/background: cover). Images from [Wikipedia](https://en.wikipedia.org/wiki/Wikipedia:Featured_pictures/Artwork/Paintings). Carousel originally by [Cyd Stumpel](https://codesandbox.io/s/9s2wd9?file=/src/App.js:1160-1168). ## Example ```svelte ``` `` can also be used with instanced or batched meshes. ## Color processing effects The `` component offers a range of properties for dynamic color processing. The properties `brightness`, `contrast`, `hue`, `saturation`, and `lightness` adjust the image's initial values additively. To decrease brightness, for instance, you would use a negative value, while a positive value would increase it. The `hue` shift is the only exception, its values range from 0 to 1, representing a complete cycle around the color wheel. Notably, values 0 and 1 are equivalent, indicating the same hue position. For the monochrome effect, specify your preferred tint using the `monochromeColor` property, and control the effect's intensity with `monochromeStrength`. Setting this strength to 0 disables the effect entirely, while a value of 1 applies it fully, rendering the image in varying shades of a single color. ### Advanced color processing with a texture The `colorProcessingTexture` property enables advanced color processing by allowing you to specify a texture that changes the strength and pattern of color effects. It can be used to create dynamic, animated effects as well as static ones that target only specified image areas. Each texture channel controls a different color processing effect: - Red for hue - Green for saturation, - Blue for lightness - Alpha for transparency. Red, green and blue channels are applied multiplicatively to the values of hue, saturation and lightness. The alpha channel acts differently, providing a flexible alpha override mechanism, that uses a range of values for dynamic image reveal effect. With changing the `alphaThreshold` property, areas with alpha values approaching 1 are revealed first, followed by regions with values tending towards 0. To further control this effect, the `alphaSmoothing` property allows for a gradual fade-in effect within a specified range. For instance, with an alphaThreshold of 0.5 and an alphaSmoothing set to 0.15, alpha values spanning from 0.5 to 0.65 will smoothly transition into visibility. Enable "color processing with a texture" in the example on top of this page to see the effect applying RGBA color processing texture can have.
*Alpha image used in the example. The lighter values towards the center are revealed first.*
### Order of processing effects 1. Alpha override 2. Brightness 3. Contrast 4. Hue, Saturation, Lightness 5. Monochrome 6. Negative --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/instance Every `` component nested in an [``](/docs/reference/extras/instanced-mesh) component resembles one instance. An `` can therefore only be used as a child component to a `` component. The `` component can be transformed and colorized individually: ```svelte ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/instanced-mesh The component `` is wrapping the Three.js object InstancedMesh and provides instanced rendering support. Use `` if you have to render a large number of objects with the same geometry and material but with different world transformations and colors. The usage of `` will help you to reduce the number of draw calls and thus improve the overall rendering performance in your application. ## Usage An `` is used in conjunction with the [``](/docs/reference/extras/instance) component: ```svelte ``` It's also possible to nest other objects in an `` component: ```svelte ``` Provide an `id` to use multiple `` components: ```svelte // Instance of InstancedMesh with id="tree" // Instance of InstancedMesh with id="leaf" ``` ## Instance count Use the property `limit` to set the maximum amount of `` components (defaults to 1000). The property `limit` will be used to initialize the internally used Float32Array. Use the property `range` to optionally limit the amount of drawn instances. ```svelte {2,3}+ ``` ## Events Mouse around in the example below. Instances also support interactivity events. ```svelte {5} ``` ## Nesting Instances can be nested in other objects and all parent transformations apply as usual: ```svelte ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/instanced-meshes The component `` takes existing `THREE.Mesh` instances and creates a `THREE.InstancedMesh` per `THREE.Mesh`. This is especially useful if you want to instantiate a lot of meshes that have been loaded with hooks like [`useGltf`](/docs/reference/extras/use-gltf). It takes the same arguments as [``](/docs/reference/extras/instanced-mesh). ## Usage ### Passing a map Load a gltf file with the [`useGltf`](/docs/reference/extras/use-gltf) hook and pass the result to the `` component. The [slot prop](https://svelte.dev/docs#template-syntax-slot) `components` can be used to instantiate a mesh multiple times. ```svelte {#if $gltf} {#snippet children({ components: { Cube } })} {/snippet} {/if} ``` When using `` with a large gltf file, be aware that `` will create a new `` for each `` in the gltf file. This can lead to a lot of `` components, which can have a negative impact on performance. You might want to filter out the meshes you want to instantiate beforehand. ### Passing an array If you don't want to use the `useGltf` hook, you can also pass an array of `THREE.Mesh` instances to the `` component. ```svelte {#snippet children({ components: [MeshA, MeshB, MeshC] })} {/snippet} ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/instanced-sprite import ComponentSignature from '$components/ComponentSignature/ComponentSignature.astro' This is an early version - features and API might change significantly over time. Please report any issues you encounter on Discord or Github. The `` component allows you to efficiently spawn large numbers of animated [sprites]() in your scene and update each instance using the `useInstancedSprite` hook, or with a `` component available through a slot prop. You can find an example of a [more complex scene here](/docs/examples/animation/complex-sprite-scene). ## `` To use the `` you must provide it with sprite metadata and a texture. While we recommend utilizing the [`buildSpritesheet()`](#buildSpritesheet) utility for this purpose, you are also free to implement your own custom solution, provided it meets the component's input requirements. Other than it's own props, `` extends and accepts all properties of [Three.js instanced mesh](https://threejs.org/docs/#api/en/objects/InstancedMesh), such as `castShadow`, `frustumCulled` etc. ```svelte ``` #### Required props | Prop | description | | ------------- | ------------------------------------------------------------------------------------------------------- | | `count` | number of instances | | `spritesheet` | Object with `spritesheet` metadata and a `texture` `{spritesheet: SpritesheetFormat, texture: Texture}` | #### Optional props | Prop | description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `autoUpdate` | Update animations automatically. It should stay `true` for most usecases. Setting to `false` is most commonly used in case of [static sprites](#static-sprites--atlassing) but it can also be used for advanced manual animation updates. | | `billboarding` | Sets the default global billboarding (sprites always facing the camera) state that is used unless the setAt was called on the instance. | | `playmode` | Sets playmode for all instances. `"FORWARD" \| "REVERSE" \| "PAUSE" \| "PINGPONG"` | | `fps` | The desired frames per second of the animation | | `alphaTest` | Sets the alpha value to be used when running an alpha test | | `transparent` | Whether or not the material should be transparent | | `randomPlaybackOffset` | Offset each sprite's animation timer by a random number of milliseconds. If `true`, randomness is within 0-100ms range. Providing the prop with a number sets the upper range of the offset - `randomPlaybackOffset={2000}` means that the animation will be offset by 0-2000ms | | `hueShift` | Changes sprite look by tweaking the material's output color by a provided hueShift, saturation and vibrance `{h: number, s: number, v:number}` | You create and update instances in three ways: 1. Utilizing the `useInstancedSprite()` hook (recommended approach). 2. Using the `` component offered through a slot prop. 3. Directly with the underlying class using the `ref` binding. ### `useInstancedSprite` The hook has to be used in a child component of `` and returns an object with following properties: - `count`: Total number of instances. - `updatePosition(id: number, position: Vector3Tuple, scale?: Vector2Tuple)`: A utility function for updating an instance's position and scale. - `animationMap`: A writable store (`Writable>`) that maps animation names to their corresponding IDs. Animation names are useful to have for setting a random animation from a pool etc. The IDs are reserved for more advanced usecases. - `sprite`: Provides direct access to the `InstancedSpriteMesh`, enabling updates to instance properties such as animations, billboarding, and play mode. ```ts import { useInstancedSprite } from '@threlte/extras' const hook = useInstancedSprite() // it's useful to immediately destructure it like this const { updatePosition, count, animationMap, sprite } = useInstancedSprite() // Examples of using the InstancedSpriteMesh API: // play animation on instance id 0 - loops by defualt sprite.play('IdleBackward').at(0) // play animation without looping sprite.play('RunLeft', false).at(1) // play animation backwards with looping sprite.play('RunLeft', true, 'REVERSE').at(2) // mesh.play is a utility that combines the use of these functions: // animation by name sprite.animation.setAt(0, 'RunBackward') // looping y/n sprite.loop.setAt(0, false) // animation direction - FORWARD (default) / REVERSE / PAUSE sprite.playmode.setAt(0, 'REVERSE') // billboarding sprite.billboarding.setAll(true) sprite.billboarding.setAt(0, true) ``` #### Typescript support The useInstancedSprite hook supports typing for autocompletion of animation names: ```ts type AnimationNames = 'walk' | 'run' | 'idle' | 'fly' const { updatePosition, count, animationMap, sprite } = useInstancedSprite() ``` ### `` Instance is a slot prop component that is used to update sprite instances properties. You can gain access to it with the `Instance` snippet prop on `InstancedSprite` component. Then put it as a child component. The only required property is `id`. It also has `position` of type `Vector3Tuple` prop and `scale` of type `Vector2Tuple`. Other than this, it as other properties that you can find in the `InstancedSpriteMesh`, so: `animationName`, `playmode`, `billboarding`, `offset`, `loop`, `flipX`, `flipY`, `frameId`. Read more about them in the [InstancedSpriteMesh](#instancedspritemesh) section The `` component serves as a declarative alternative to `useInstancedSprite` hook to dynamically update the properties of sprite instances within the `InstancedSprite` component. You can access through the `Instance` snippet prop. _Example: Set a position, scale and flipX for every instance._ ```svelte {#snippet children({ Instance })} {#each { length: 10000 } as _, i} {/each} {/snippet} ``` '} signature={{ props: [ { name: 'id', type: 'number', required: true, description: 'Instance id. Ranges between 0 and the `count` value of InstacedSpriteMesh.' }, { name: 'animationName', type: 'string', required: false, description: 'Sets an active animation by name.' }, { name: 'position', type: 'Vector3Tuple', required: false, description: 'Sets the position of an instance.' }, { name: 'scale', type: 'Vector2Tuple', required: false, description: 'Sets the scale of an instance.' }, { name: 'playmode', type: '"FORWARD" | "REVERSE" | "PAUSE" | "PINGPONG"', required: false, description: 'Sets the playmode of an instance.' }, { name: 'billboarding', type: 'boolean', required: false, description: 'Toggles billboarding on/off.' }, { name: 'offset', type: 'number', required: false, description: 'Sets the exact time value in ms by which the animation playback is offset.' }, { name: 'loop', type: 'boolean', required: false, description: 'Toggles looping of the animation on/off. If off, the last played frame is displayed when the animation completes.' }, { name: 'flipX', type: 'boolean', required: false, description: 'Toggles flipping the sprite horizontally.' }, { name: 'flipY', type: 'boolean', required: false, description: 'Toggles flipping the sprite vertically.' }, { name: 'frameId', type: 'number', required: false, description: `Sets an exact frame to display. If animationName is set, it's the id of the frame in that given animation, otherwise it's a spritesheet frame id.` } ] }} /> ### binding The `InstancedSpriteMesh` class is the foundation behind the `` component, written to enable efficient instancing of animated sprites within a Three.js environment. The `` component lets you bind to it through `ref`. The class extends the capabilities of the [troika's InstancedUniformsMesh](https://protectwise.github.io/troika/three-instanced-uniforms-mesh/). For an in-depth exploration of `InstancedSpriteMesh` and its features, refer to the documentation available at [InstancedSpriteMesh docs](https://three-kit.vercel.app/instancedsprite/01-instanced-sprite-mesh/). ## Spritesheets ### SpritesheetMetadata Object used in `buildSpritesheet` function has to be compliant with the `SpritesheetMetadata` type format. This type is structured to accommodate the metadata for one or multiple sprite files within a single spritesheet. ```ts type SpritesheetMetadata = { url: string type: 'rowColumn' | 'frameSize' width: number height: number animations: { name: string frameRange: [number, number] }[] }[] ``` #### Understanding SpritesheetMetadata A `SpritesheetMetadata` is an array, with each entry representing metadata fields for one sprite: - `url`: Specifies the path or URL to the sprite image file. - `type`: Determines the method of defining the spritesheet dimensions. Type `"rowColumn"` specifies the layout in terms of rows and columns within the image, and type `"frameSize"` instead defines the size of each frame, allowing the utility to calculate the layout. - `width` and `height`: Depending on type, these refer to the number of `columns` and `rows` (`"rowColumn"`) or the dimensions of a single frame (`"frameSize"`). - `animations`: An array detailing the animations, where each animation has a name and a `frameRange`. The `frameRange` is a tuple marking the start and end frames of the animation. #### Typesafety For improved developer experience when working with TypeScript, it is strongly recommended to use `as const` assertion in combination with `satisfies SpritesheetMetadata`. This approach not only ensures compliance with the `SpritesheetMetadata` type but also enables autocompletion for animation names within utility functions, which is highly recommended: ### `buildSpritesheet()` `buildSpritesheet()` is a utility function for building a final texture and spritesheet object from a provided `SpritesheetMetadata` object or external source. Each `buildSpritesheet` method return an Promise that and has to be awaited. Promise returned by each method contains an object with a `spritesheet` ready for use in ``. #### buildSpritesheet().from(meta: SpritesheetMetadata) Other than `spritesheet` promise, it also returns a `useInstancedSprite` hook. This hook can be enhanced with extra typescript support for autocompletion of animation names as such: ```ts const meta = [ { url: '/textures/sprites/cacodaemon.png', type: 'rowColumn', width: 8, height: 4, animations: [ { name: 'fly', frameRange: [0, 5] }, { name: 'attack', frameRange: [8, 13] }, { name: 'idle', frameRange: [16, 19] }, { name: 'death', frameRange: [24, 31] } ] } ] as const satisfies SpritesheetMetadata const result = buildSpritesheet.from(meta) ``` ![Tree sprite atlas](/images/docs/extras/instanced-sprite/useInstancedSpriteAutocomplete.png) #### buildSpritesheet().fromAseprite(asepriteDataUrl: string, spriteImageUrl: string) Similar to above, but it parses the Aseprite metadata json into the correct format. Does not provide any additional utilities. ### Examples #### Multiple animations ```ts const demonSpriteMeta = [ { url: '/textures/sprites/cacodaemon.png', type: 'rowColumn', width: 8, height: 4, animations: [ { name: 'fly', frameRange: [0, 5] }, { name: 'attack', frameRange: [8, 13] }, { name: 'idle', frameRange: [16, 19] }, { name: 'death', frameRange: [24, 31] } ] } ] as const satisfies SpritesheetMetadata ``` #### Multiple files ```ts const goblinSpriteMeta = [ { url: '/textures/sprites/goblin/Attack.png', type: 'rowColumn', width: 8, height: 1, animations: [{ name: 'attack', frameRange: [0, 7] }] }, { url: '/textures/sprites/goblin/Death.png', type: 'rowColumn', width: 4, height: 1, animations: [{ name: 'death', frameRange: [0, 3] }] }, { url: '/textures/sprites/goblin/Idle.png', type: 'rowColumn', width: 4, height: 1, animations: [{ name: 'idle', frameRange: [0, 3] }] } ] as const satisfies SpritesheetMetadata ``` ## Static sprites & Atlassing This component focuses on targetting animated sprites, but it's possible to use it for static images as well. If each frame of the spritesheet is a separate animation, then it effectively acts as an [atlas](https://en.wikipedia.org/wiki/Texture_atlas) with named sprites. The `` component in the example above does this. ![Tree sprite atlas](/textures/sprites/trees-pixelart.png) Set `autoUpdate={false}` on static components and only update it manually with `sprite.update()`. This has to be done when the InstancedSprite is initiated or when spritesheet or atlas change. If you don't do it, then the spritesheet will run animation updates each frame to run animations that don't really exist. --- ## interactivity Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/interactivity To add click, pointer and wheel events to your Threlte app use the `interactivity` plugin. ```svelte title="Scene.svelte" {2,3}+ ``` All child components can now make use of the new events. ```svelte title="Scene.svelte" {7-9}+ { console.log('clicked') }} > ``` ## Available events The following interaction events are available: ```svelte console.log('click')} oncontextmenu={(e) => console.log('context menu')} ondblclick={(e) => console.log('double click')} onwheel={(e) => console.log('wheel')} onpointerup={(e) => console.log('up')} onpointerdown={(e) => console.log('down')} onpointerover={(e) => console.log('over')} onpointerout={(e) => console.log('out')} onpointerenter={(e) => console.log('enter')} onpointerleave={(e) => console.log('leave')} onpointermove={(e) => console.log('move')} onpointermissed={(e) => console.log('missed')} /> ``` ## Event data All interaction events contain the following data: ```typescript type Event = THREE.Intersection & { // Inherited from THREE.Intersection: object: THREE.Object3D // The object that was actually hit distance: number // Distance from the ray origin to the intersection point: THREE.Vector3 // The intersection point in world coordinates face: THREE.Face | null // The intersected face // ... and other THREE.Intersection properties (uv, normal, instanceId, etc.) // Added by interactivity: eventObject: THREE.Object3D // The object that registered the event intersections: Intersection[] // All intersections, including the eventObject that registered each handler camera: THREE.Camera // The camera used for raycasting delta: number // Distance in pixels between the pointerdown position and this event's position (0 for non-click events) nativeEvent: MouseEvent | PointerEvent | WheelEvent // The native browser event pointer: Vector2 // The pointer position in normalized device coordinates ray: THREE.Ray // The ray used for raycasting stopPropagation: () => void // Stops the event from being delivered to farther objects stopImmediatePropagation: () => void // Delegates to the native DOM event, blocking all further listeners (e.g. OrbitControls) stopped: boolean // Whether the event propagation has been stopped } ``` ## Event propagation Propagation works a bit differently to the DOM because objects can occlude each other in 3D. The intersections array in the event data includes all objects intersecting the ray, not just the nearest. Only the first intersection with each object is included. The event is first delivered to the object nearest the camera, and then bubbles up through its ancestors like in the DOM. After that, it is delivered to the next nearest object, and then its ancestors, and so on. This means objects are transparent to pointer events by default, even if the object handles the event. `event.stopPropagation()` doesn't just stop this event from bubbling up, it also stops it from being delivered to farther objects (objects behind this one). All other objects, nearer or farther, no longer count as being hit while the pointer is over this object. If they were previously delivered pointerover events, they will immediately be delivered pointerout events. If you want an object to block pointer events from objects behind it, it needs to have an event handler as follows: ```svelte e.stopPropagation()} /> ``` even if you don't want this object to respond to the pointer event. If you do want to handle the event as well as using `stopPropagation()`, remember that the `pointerout` events will happen during the `stopPropagation()` call. You probably want your other event handling to happen after this. ## Touch interactions On touch devices, the browser may cancel `pointermove` events mid-gesture when it decides to use the touch for its own navigation (scrolling, pinch-zoom, swipe-back, etc.). This is controlled by the CSS [`touch-action`](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) property. When the browser takes over a touch, it fires a [`pointercancel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointercancel_event) event and stops delivering `pointermove` updates — meaning `onpointermove` handlers on your 3D objects will stop receiving events during touch-and-drag interactions. If your canvas is the primary interaction surface (e.g. a fullscreen 3D experience or a dedicated viewport), you can fix this by setting `touch-action: none` on a parent element wrapping the `` component. This tells the browser not to intercept any touch gestures, ensuring pointer events fire reliably throughout the entire interaction. ```svelte title="App.svelte"
``` Only use `touch-action: none` when the canvas is the primary interaction surface. For canvases embedded in scrollable pages, this will prevent users from scrolling past the canvas with touch. In that case, consider more targeted values like `touch-action: pan-y` (allows vertical scrolling but captures horizontal gestures) or handle the trade-off in your application logic. ## Event targets If no event target is specified, all event handlers listen to events on the `domElement` of the `renderer` (which is the canvas element by default). You can specify a different target by passing a `target` prop to the `interactivity` plugin. ```svelte title="Scene.svelte" ``` It's also possible to change the target at runtime by updating the store `target` returned from the `interactivity` plugin. ```svelte title="Scene.svelte" ``` ## Compute function In the event that your event target is not the same size as the canvas, you can pass a `compute` function to the `interactivity` plugin. This function receives the DOM event and the interactivity state and should set the `pointer` property of the state to the pointer position in normalized device coordinates as well as set the raycaster up for raycasting. ```svelte title="Scene.svelte" ``` ## Event filtering You can filter and sort events by passing a `filter` to the `interactivity` plugin. The function receives all hits and the interactivity state and should return the hits that should be delivered to the event handlers in the order they should be delivered. ```svelte title="Scene.svelte" ``` ## Interactivity state To access the interactivity state, you can use the `useInteractivity` hook in any child component of the component that implements the `interactivity` plugin as follows: ```svelte title="Child.svelte" ``` where this is the type of the interactivity state: ```ts export type State = { enabled: CurrentWritable target: CurrentWritable pointer: CurrentWritable pointerOverTarget: CurrentWritable lastEvent: MouseEvent | WheelEvent | PointerEvent | undefined raycaster: Raycaster initialClick: [x: number, y: number] initialHits: THREE.Object3D[] hovered: Map> interactiveObjects: THREE.Object3D[] compute: ComputeFunction filter?: FilterFunction clickDistanceThreshold: number clickTimeThreshold: number } ``` [`CurrentWritable`](/docs/reference/core/utilities#currentwritable) is a custom Threlte store. It's a regular writable store that also has a `current` property which is the current value of the store. It's useful for accessing the value of a store in a non-reactive context, such as in loops. ## TypeScript ### Prop types By default, the `interactivity` plugin does not add any prop types to the `` component. You can however extend the types of the `` component by defining the `Threlte.UserProps` type in your ambient type definitions. In a typical SvelteKit application, you can find these [in `src/app.d.ts`](https://svelte.dev/docs/kit/types#app.d.ts). The interactivity plugin exports the `InteractivityProps` type which you can use as shown below: ```ts title="src/app.d.ts" import type { InteractivityProps } from '@threlte/extras' declare global { namespace Threlte { interface UserProps extends InteractivityProps {} } } export {} ``` Now all event handlers on `` components will be type safe. ```svelte title="Scene.svelte" { // e: IntersectionEvent }} /> ``` --- ## layers Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/layers `layers` is a plugin that provides inheritance for the property `layers` on `` components. Typically when assigning a value to `layers` on a `` component, it will only be applied to that component. This plugin allows you to assign a value to `layers` on a parent component and have it be inherited by all child components. This plugin injects and relies on a context. It's affecting all `` components that are descendants of the component that uses it. Check out the [guide on structuring your app](/docs/learn/basics/app-structure) to learn more about contexts. ## Usage ```svelte title="Scene.svelte" {2,3}+ ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/linear-gradient-texture A reactive linear gradient texture. The underlying texture uses an [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas) and a [CanvasTexture](https://threejs.org/docs/index.html#api/en/textures/CanvasTexture) and is assigned the same [colorspace](https://threejs.org/docs/index.html#api/en/textures/Texture.colorSpace) as the renderer. ## Attaching the Texture The texture is automatically attached to the `map` property of its parent. You can disable this behaviour by setting the `attach` prop to `false`. This may be useful if you want to create the texture but use it somewhere else. ```svelte ``` ## Gradient Stops `` accepts a `stops` prop which is an array of color stops that define the gradient. A stop is defined by two things; an `offset` and a `color`. Gradient stops are identical to how you would use them with a [CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createLinearGradient#examples), notably the `offset` should be a number between 0 and 1 inclusive. Stop colors can be any valid color representation in Three.js. Here are a couple examples of valid stops. ```svelte ``` You can even mix and match color representations ```svelte ``` All of the colors above are valid representations of the color red. ## Gradient Start Point and End Point You can control the gradient start point and end point with the `startX`, `startY`, `endX`, and `endY` props. For example, the props for a gradient that starts at the bottom left corner of the texture and ends at the top right corner would be: ```svelte ``` ## Adjusting Scene Colors If the colors in your scene do not match the color in your stops, you may need to adjust the tone mapping of the scene. `ToneMapping` constants are imported from the three library. ```svelte ... ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/mask Masks use the stencil buffer to cut out areas of the screen. This component is a port of [drei's `` component](https://github.com/pmndrs/drei#mask). The Mask component requires Three.js to render with a stencil buffer. As of [r163](https://github.com/mrdoob/three.js/releases/tag/r163), stencil is set to `false` by default. To enable the stencil buffer, set it in your canvas's renderer: `{return new WebGLRenderer({canvas,stencil: true })}}>`. In prior versions the default is `true` already. First you need to define a mask, give it the shape that you want. ```svelte ``` Now refer to it with the `useMask` hook and the same id, your content will now be masked out by the geometry defined above. ```svelte ``` You can build compound masks with multiple shapes by re-using an id. You can also use the mask as a normal mesh by providing colorWrite and depthWrite props. ```svelte ``` Invert masks individually by providing a 2nd boolean argument to the useMask hook. ```ts const stencil = useMask(1, true) ``` --- ## meshBounds Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/mesh-bounds A raycast function that will first check for collision with the object's bounding sphere, then it will check against the object's bounding box but **only** if the bounding box has been previously computed. You can use this function if you want to trade pointer precision for an increase in performance. ## Usage ### Basic Usage `meshBounds` can simply be passed to an object's [`raycast`](https://threejs.org/docs/index.html?q=mesh#api/en/objects/Mesh.raycast) prop. ```svelte ``` If your meshes or models aren't very complex in terms of geometry, `meshBounds` won't provide much of a performance boost. ### Creating a plugin Instead of manually applying the `meshBounds` raycast function to each mesh, you can use a [Threlte plugin](/docs/learn/advanced/plugins) to automatically apply the `meshBounds` raycast function to all meshes in your scene. ```svelte title="Scene.svelte" ``` To selectively apply the `meshBounds` raycast function to only certain meshes, you can listen to a prop (e.g. `raycastMeshBounds`) and conditionally apply the function. ```svelte title="Scene.svelte" ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/mesh-discard-material `` is a material that renders nothing. It does so by discarding everything in the fragment shader. The difference between `` and `` is that shadows and children are still visible when using ``. In the example above, the visible mesh does not use `` nor sets `visible={false}`. The center mesh sets `visible={false}`. Notice how its shadow is missing. The final mesh uses `` and its shadow is visible even though the mesh itself isn't. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/mesh-line-geometry Used in combination with [``](/docs/reference/extras/meshline-material) to create a line formed of a strip of billboarded triangles, based on THREE.MeshLine. ## Usage This component works by taking an array of points to form a line geometry without any thickness, which is then projected and expanded in screen space by ``. Both `` and `` need to be the children of the same parent mesh. ### Example ```svelte ``` ### Points The `points` property is required and accepts an array of `THREE.Vector3` objects. The `points` property is reactive and you can animate the line by updating the positions of the `Vector3` objects within the array. When updating the points array it is strongly recommended that the updated array is the same length as the initial array, because when the array is resized many `BufferAttribute`s are re-created. ### Shape By default the line will be a constant thickness along it's length, at a width defined in ``. To create a line that tapers down to a point at each end you can set the `shape` property to `'taper'`. For more control over the shape of the line you can set the `shape` property to `'custom'` and pass a custom function to the `shapeFunction` property. The function will define the width at each point along the line, for example `p => 2` will make the line double the width. The property `p` is the percentage of the number of points, i.e. for point 200 of 250 points, p = 0.8. For example the following code will define a line that tapers off at one end: ```svelte 1 - p} /> ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/mesh-line-material Used in combination with [``](/docs/reference/extras/meshline-geometry) to create a line formed of a strip of billboarded triangles, based on THREE.MeshLine. ## Usage This component works by taking a line geometry from `` and projecting and expanding the vertices in screen space. Both `` and `` need belong to the same parent mesh. ### Example ```svelte ``` ### Width and color By default the line will be white and have a width of 1. The `width` property will use world units and scale correctly with other objects in your scene. If you would like the line to be a fixed size regardless of distance from the camera you can set the `attenuate` property to `false`. ### Opacity and dashes Just like other materials in Three.js you need to set `transparent` to `true` for opacity to have any effect. You must also set `transparent` to `true` for if you are using dashed lines. You can use a combination of `dashArray`, `dashRatio` and `dashOffset` to create dashed lines. If you're rendering transparent lines, dashed lines or lines with an alpha map you can avoid issues where the line overlaps itself by setting depthTest to false. ### Alpha map You can pass a texture to the `alphaMap` property to use as an alpha mask along the length of the line, where black is invisible and white is visible. In the example below we load a paint brush texture with the `useTexture` hook. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/mesh-refraction-material To use this component you need to install the separate library `three-mesh-bvh`, please run `npm install three-mesh-bvh` before adding this component to your project. This material may not work reliably on some devices or browsers. We're investigating possible fixes. This component is a port of [drei's `` component](https://github.com/pmndrs/drei#meshrefractionmaterial), a convincing Glass/Diamond refraction material. ## Examples ### Basic Example You can either pass in a texture to use as the environment: ```svelte title="RefractionWithTexture.svelte" {#await env then texture} {/await} ``` or you can use a cube camera to generate the environment: ```svelte title="RefractionWithCubeCamera.svelte" ``` --- ## onReveal Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/onreveal `onReveal` invokes a callback when the component is revealed (i.e., no longer suspended in the context of a [``](/docs/reference/extras/suspense) boundary). It mimics Svelte's lifecycle method `onMount`. If there is no `` component, the callback will be executed with Svelte's `onMount` as the component will never suspend. `onReveal` is mimicking Svelte's `onMount` and can be used in its place for triggering animations, etc., within the boundaries of a `` component. If it's used outside of a `` component, it will behave just like Svelte's `onMount`. This means that ## Example The following component loads a model with the hook `useGltf` and is potentially wrapped in a `` boundary. ```svelte {#await gltf then { scene }} {/await} ``` You may also return a function from the callback to be executed when the component is unmounted or the component is suspended again. ```ts onReveal(() => { console.log('The component has been revealed') return () => { console.log('The component has been unmounted or suspended again') } }) ``` --- ## onSuspend Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/onsuspend `onSuspend` invokes a callback when the component becomes suspended within the boundaries of a [``](/docs/reference/extras/suspense) component. If there is no `` component, the callback will never be executed, as the component will never suspend. ## Example The following component loads a model with the hook `useGltf` and is potentially wrapped in a `` boundary. ```svelte {#await gltf then { scene }} {/await} ``` `onSuspend` can be used to execute additional logic when a component becomes suspended within a `` boundary. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/orbit-controls `` allow the camera to orbit around a target while ensuring a fixed camera up vector. If the camera orbits over the "north" or "south" poles, the camera experiences a "gimbal lock" that forces the scene to rotate until it is rightside up. This type of camera control is commonly used for showing off 3D models of products because it prevents them from ever appearing upside down. For an alternative camera controller, see [``](/docs/reference/extras/trackball-controls). If placed as a child of a camera component, `` will attach to that camera. Otherwise, it attaches to the scene's default camera. A camera can also be passed explicitly via the `camera` prop. This example shows off just a few of the configurable properties of ``. To see all 30+ properties, consult the [Three.js docs](https://threejs.org/docs/#examples/en/controls/OrbitControls). ## Usage ```svelte ``` `` is a light wrapper that will use its parent as the target camera (or the default camera if not a child of one) and the DOM element the renderer is rendering to as the DOM element to listen to pointer events. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/outlines A port of the drei [``](https://github.com/pmndrs/drei?tab=readme-ov-file#outlines) component. An ornamental component that extracts the geometry from its parent and displays an [inverted-hull outline](https://bnpr.gitbook.io/bnpr/outline/inverse-hull-method). Supported parents are `Mesh`, `SkinnedMesh` and `InstancedMesh`. Model: Battle Damaged Sci-fi Helmet by [theblueturtle\_](https://sketchfab.com/theblueturtle_) ### Example ```svelte ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/perf-monitor Utility component for monitoring basic rendering statistics using [three-perf](https://github.com/TheoTheDev/three-perf). ## Usage Simply drop in the `` component as a child of Threlte ``. The component starts measuring before `mainStage` and ends after `renderStage`. Learn more about tasks in Threlte [here](/docs/learn/basics/scheduling-tasks) ```svelte ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/points-material An extended `THREE.PointsMaterial` that renders antialiased round dots. Potted plant by [Federica Girola](https://sketchfab.com/3d-models/luminous-pointcloud-plant-in-a-pot-34cf80b0bdb7425d85426e90eace3fc4). ## Usage ```svelte ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/portal A component that renders its children as children of an object that can exist **anywhere in your Threlte application**. You can use the prop `id` to render into a ``. Although Portals are extremely helpful in certain situations, it can be hard to reason about them at times. It's recommended to use them sparingly. ### You might not need `` Some objects such as the [`THREE.DirectionalLightHelper`](https://threejs.org/docs/index.html#api/en/helpers/DirectionalLightHelper) need to be added to the scene itself to be functional. In this case, the portal target is easily accessible, and passing the `scene` object to the `` component's [attach](http://localhost:4321/docs/reference/core/t#attach) property can accomplish everything we need. ### Rendering to a `` For more complex cases where it's hard to query the target, using `` may be an easier solution. You can define where a `` should render its children by using the component [``](/docs/reference/extras/portal-target). --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/portal-target A component that defines a target for a [``](/docs/reference/extras/portal) component. Although Portals are extremely helpful in certain situations, it can be hard to reason about them at times. It's recommended to use them sparingly. ## Example ```svelte title="ComponentA.svelte" ``` ```svelte title="ComponentB.svelte" ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/positional-audio Creates a positional audio entity. This uses the [Web Audio API](https://developer.mozilla.org/en-US/Web/API/Web_Audio_API). You need to have an `` component in your scene in order to use ` Music: [legrisch](https://legrisch.com) ### Example ```svelte ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/radial-gradient-texture A reactive radial gradient texture that attaches to the "map" property of its parent. The underlying texture uses an [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas) and a [CanvasTexture](https://threejs.org/docs/index.html#api/en/textures/CanvasTexture) and is assigned the same [colorspace](https://threejs.org/docs/index.html#api/en/textures/Texture.colorSpace) as the renderer. ## Attaching the Texture The texture is automatically attached to the `map` property of its parent. You can disable this behaviour by setting the `attach` prop to `false`. This may be useful if you want to create the texture but use it somewhere else. ```svelte ``` ## Radius Props The `innerRadius` and `outerRadius` props control the size of the gradient. The `innerRadius` prop should be less than the `outerRadius` prop but it is not enforced. If `outerRadius` is set to `'auto'` the `outerRadius` is effectively set to the radius of the circle that circumscribes the canvas. For example, if the canvas's width and height are **1024**, and `outerRadius` is set to `'auto'`, the radius that will be used is **sqrt(1024\*\*2 + 1024\*\*2)** or roughly **724**. It is also not enforced that `innerRadius` and `outerRadius` are both positive. ## Gradient Stops `` accepts a `stops` prop which is an array of color stops that define the gradient. A stop is defined by two things; an `offset` and a `color`. Gradient stops are identical to how you would use them with a 2D context, notably the `offset` should be a number between 0 and 1 inclusive. Stop colors must be any acceptable CSS string. Here are a couple examples of valid stops. ```svelte ``` Three.js's Color class conveniently has a `getHexString` method which you can use to convert a color to a css color string. ```svelte ``` ## Adjusting Scene Colors If the colors in your scene do not match the color in your stops, you may need to adjust the tone mapping of the scene. `ToneMapping` constants are imported from the three library. ```svelte ... ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/resize Scales up or down a group of objects by the maximum dimension of their bounding box. Object proportions are preserved. This is particularly useful if you want to "normalize" the dimensions of an object to be in the range 0 to 1. ## Choosing the Axis You can choose the axis by providing the `axis` prop, otherwise the maximum axis is used. ```svelte ``` If you use the `axis` prop, the dimensions will not be normalized to the range 0 to 1 unless the maximum axis is chosen. ## Bring Your Own Box Use the `box` prop to provide your own Box3 instance. Doing so allows you to capture the bounding box of the objects prior to any scaling ```svelte ``` ## Normalizing Multiple Objects If you have a bunch of source objects that are all different sizes, a useful technique is to wrap each one in a ``. Once they've all been normalized, it may be easier to reason about their relative sizes. In the example above, the fox model is much larger than the duck and flower models. It is so much larger that you may have to pull the camera back in order to see it. Using `` scales each model so that each one fits inside a unit cube. From there, their relative sizes and positions may be easier to work with. ## Manual Resizing ### Using the Export You can manually trigger a resize by calling the `resize` export. ```svelte title="Scene.svelte" ``` ### Using the Snippet Argument You can also use the snippet argument to trigger a resize. ```svelte title="Scene.svelte" {#snippet children({ resize })} {/snippet} ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/rounded-box-geometry Creates a rounded box geometry with a Three.js ExtrudeGeometry. ### Example ```svelte ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/shadow-alpha This component is a port of [drei's `` component](https://github.com/pmndrs/drei#shadowalpha). By default in Three.js, shadows are always fully opaque regardless of the material's opacity or alpha map. `` fixes this by making a mesh's shadow respect its material's `opacity` and `alphaMap`. Place it as a child of the mesh whose shadow you want to control: ```svelte ``` The shadow will now be 50% transparent, matching the material's opacity. ### Alpha Maps If the parent material has an `alphaMap` (e.g. a leaf texture with cutouts), the shadow will automatically match those cutouts: ```svelte ``` You can override the alpha map or disable it: ```svelte ``` ### How It Works `` uses [Bayer dithering](https://en.wikipedia.org/wiki/Ordered_dithering) in custom depth and distance materials. Fragments below the opacity threshold are discarded from the shadow map, creating a pattern that approximates transparency. This is a screen-space technique, so the dither pattern may be visible at close range. This component is a workaround for a [long-standing Three.js limitation](https://github.com/mrdoob/three.js/issues/10600). It will be deprecated when Three.js adds native support for shadows from transparent objects. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/shadow-material A cheap [CanvasTexture](https://threejs.org/docs/?q=canvastexture#api/en/textures/CanvasTexture)-based material meant to look like a shadow without the need for any lights in the scene. ## Transparency Because the canvas texture uses an alpha channel, `` uses transparency by default. This means it will be sorted into a "transparent objects" render list by the renderer. In three.js, transparent objects are rendered _after_ opaque objects. This may be something to keep in mind when using ``. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/sky This component adds a [Three.js `Sky` object](https://github.com/mrdoob/three.js/blob/dev/examples/jsm/objects/Sky.js) to the scene, renders that on-demand to a cubemap which is assigned to the default scene as the environment map. ## Usage ```svelte { ref.lookAt(0, 0, 0) }} /> ``` ## Environment By default, this component will render the sky to the scene environment. This can be disabled by setting the `setEnvironment` prop to `false`. ```svelte ``` ## Performance The `` component will only re-render the cubemap when the properties change. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/soft-shadows This component is a port of the component [`` from drei](https://github.com/pmndrs/drei/blob/master/src/core/softShadows.tsx). It injects Percentage-Closer Soft Shadows (PCSS) into Three.js' shader chunk. Mounting and unmounting this component will lead to all shaders being be re-compiled, although it will only cause overhead if `` is mounted after the scene has already rendered, if it mounts with everything else in your scene shaders will compile naturally. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/sparkles This component is a port of the component [`` from drei](https://github.com/pmndrs/drei/blob/master/src/core/Sparkles.tsx). Adds shader-based particles to your scene. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/stars This component is a port of [drei's `` component](https://github.com/pmndrs/drei#stars), which adds a blinking shader-based starfield to your scene. ## Examples ### Basic Example ```svelte title="Billboard.svelte" ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/suspense The component `` allows you to orchestrate the loading of resources inside (nested) child components. The implementation roughly follows a subset of the concept established by the [React Suspense API](https://react.dev/reference/react/Suspense) and has certain limitations. The idea is to allow a parent component to make decisions based on the _loading state_ of child components. The parent component can then decide to show a loading indicator or a fallback component while the child component is loading. ## Usage ### In a child component Let's have a look at a simple component that loads a model with the hook `useGltf`. ```svelte title="Model.svelte" {#await gltf then { scene }} {/await} ``` We can make that component _suspense-ready_ by using the hook `useSuspense` and passing the promise returned by `useGltf` to it. ```svelte {3,6}m {5}+ title="Model.svelte" {#await gltf then { scene }} {/await} ``` *Suspense-ready* means it has the ability to hit a `` boundary if there is any. If there's no `` boundary, the component will behave as usual. ### In a parent component Now we implement the component "Model.svelte" in a parent component: ```svelte title="Parent.svelte" ``` To let the parent component decide what to do while the model component is loading, we wrap the child component in a `` component and show a fallback component while the child component is loading. ```svelte {3,4,7,8,10}+ title="Parent.svelte" {#snippet fallback()} {/snippet} ``` In contrast to the React Suspense API, the `` component also acts as an error boundary for async requests made in child components wrapped in `suspend`. The `"error"` snippet will be mounted as soon as an error is thrown in a child component wrapped in `suspend` and the snippet prop `errors` can be used to display a meaningful error message. ## UX considerations The `` component is a great tool to improve the user experience of your application. However, it is important to consider the following points: 1. The `` component will only ever show one of the available slots at any time: - The snippet `"error"` will be shown as soon as an error is thrown. - The snippet `"fallback"` will be shown as long as a child component is suspended. - The default slot will be shown as long as no child component is suspended and no error has been thrown. 2. Umounting a component that suspends rendering through awaiting a resource or throwing an error will discard its suspend state. 3. Directly citing the [React Suspense API](https://react.dev/reference/react/Suspense#revealing-nested-content-as-it-loads): > Don't put a Suspense boundary around every component. Suspense boundaries should not be more granular than the loading sequence that you want the user to experience. 4. A `` component can be re-suspended if a child component invokes `suspend` again. The property `final` on the component `` can be used to prevent this behavior. ## Limitations 1. It's important to keep in mind that while the contents of the default slot are not visible, they are still mounted and the component is otherwise fully functional. This means that any side effects (for instance `useTask` hooks) that are invoked in a _suspense-ready_ child component will be executed immediately. ```svelte title="Model.svelte" {#await gltf then { scene }} {/await} ``` 2. Also, in contrast to the React Suspense API, the `` component does not support showing a stale version of a child component while it is loading. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/svg Renders an SVG using Three.js' SVGLoader. ## Examples ### Basic Example ```svelte title="Scene.svelte" ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/text The `` component uses [troika-three-text](https://github.com/protectwise/troika/tree/master/packages/troika-three-text) to render text. ### Example ```svelte ``` `` is [suspense-ready](/docs/reference/extras/suspense) and will suspend while the font is loading. You can use the `characters` prop to preload a specific set of characters to prevent [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content). ```svelte {#snippet fallback()} {/snippet} ``` --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/text-3d-geometry Render 3D text as a geometry using Three.js' [TextGeometry](https://threejs.org/docs/index.html#examples/en/geometries/TextGeometry). ## Examples ### Basic Example ```svelte title="Scene.svelte" ``` ### Using a Custom Font If no `font` property is provided, the default font "Helvetiker" will be used and loaded from the CDN [JSDeliver](https://cdn.jsdelivr.net/npm/three/examples/fonts/helvetiker_regular.typeface.json). If you want to use a custom font, you can generate a font using [typeface.js](https://gero3.github.io/facetype.js/). Provide the path to the resulting JSON file using the prop `font`. ### Suspense-Ready The component `` is _suspense-ready_. Using it in a [`` boundary](/docs/reference/extras/suspense) will suspend rendering until the provided font is loaded: ```svelte title="Scene.svelte" {#snippet fallback()} {/snippet} ``` ### Loading a Font Yourself You can also load the font yourself and pass it to the component, like so: ```svelte title="Scene.svelte" {#if $font} {/await} ``` ### Centering Text You can center a text by using the [`` component](/docs/reference/extras/align) and calling the align slot prop when the text geometry is created. ```svelte title="Scene.svelte" {#snippet children({ align })} {/snippet} ``` ### Smoothing Text You can smooth the text by setting the `smooth` prop to a value above 0 to smooth all edges where the angle between faces is less than the `smooth` value. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/trackball-controls `` allow the camera to orbit freely around a target without causing gimbal lock. This type of camera controller is commonly used when the concepts of up and down are less important than the ability to carefully inspect a model from every angle. For an alternative camera controller, see [``](/docs/reference/extras/orbit-controls). If placed as a child of a camera component, `` will attach to that camera. Otherwise, it attaches to the scene's default camera. A camera can also be passed explicitly via the `camera` prop. By default, damping is enabled. You can disable this by setting `staticMoving` to true. Unlike ``, `` does not support `autoRotate`. This example shows off just a few of the configurable properties of ``. To see all 15+ properties, consult the [Three.js docs](https://threejs.org/docs/#examples/en/controls/TrackballControls). ## Usage ```svelte ``` `` is a light wrapper that will use its parent as the target camera (or the default camera if not a child of one) and the DOM element the renderer is rendering to as the DOM element to listen to. It will also by demand invalidate the frame loop. --- ## Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/transform-controls This component can be used to transform objects in 3D space by adapting a similar interaction model of DCC tools like Blender. Unlike other controls, it is not intended to transform the scene's camera. The component `` needs to be the parent of the component to be transformed. ## Camera Controls When using camera controls alongside ``, dragging a transform gizmo would also move the camera. To prevent this, `` automatically pauses any registered camera controls while dragging. The following controls are handled automatically (no extra setup needed): - `` - `` - `` You can disable this with `autoPauseControls={false}`. ### Third-party controls For custom or third-party controls, pass them via the `cameraControls` prop. Any object with an `enabled` property works: ```svelte ``` ## Examples ### Basic usage ```svelte title="Scene.svelte" ``` ### Transforming an external object The `` component can also transform an object passed to it: ```svelte title="Scene.svelte" {#snippet children({ ref })} {/snippet} ``` --- ## transitions Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/transitions The plugin `transitions` uses Svelte internals. Changes to the runtime of Svelte may break this plugin. If you encounter any issues, please [open an issue on GitHub](https://github.com/threlte/threlte/issues). It's recommended to lock the version of Svelte to a specific version. The plugin `transitions` enables [Svelte-like transitions](https://svelte.dev/docs/svelte/transition) on Threlte components. ```svelte {#if visible} {/if} ``` ## Usage To use Threlte transitions, you need to inject the [plugin](/docs/reference/core/plugins) first via invoking `transitions()`. All child `` components will then accept the transition properties `in`, `out` and `transition`. ### `createTransition` Threlte Transitions use regular [Svelte transitions](https://svelte.dev/docs/svelte/transition) under the hood and therefore provide a similar API. The function `createTransition` is used to conveniently create a transition. To create a transition, you need to provide a function which accepts a reference to the object referenced by the `` component and returns an object with the following properties: - `duration`: The duration of the transition in milliseconds. - `tick`: A function that is called on every tick of the transition. - `easing` (optional): The easing function to use. - `delay` (optional): The delay of the transition in milliseconds. ```ts import { isInstanceOf } from '@threlte/core' import { createTransition } from '@threlte/extras' import { cubicOut } from 'svelte/easing' const fade = createTransition((ref) => { // Only apply the transition to materials if (!isInstanceOf(ref, 'Material')) return // Make the material transparent if it's not already if (!ref.transparent) { ref.transparent = true ref.needsUpdate = true } return { tick(t) { // t is [0, 1] ref.opacity = t }, easing: cubicOut, duration: 400, delay: 100 } }) ``` The transition `fade` can now be applied to all `` components that instantiate classes extending `THREE.Material` like `THREE.MeshBasicMaterial` or `THREE.MeshStandardMaterial`: ```svelte ``` ### Transition Directions Run a transition only when the component mounts: ```svelte ``` Run a transition only when the component unmounts: ```svelte ``` Run a transition when the component mounts or unmounts: ```svelte ``` To react on different transition directions in the same transition, you can use the `direction` parameter: ```ts import { createTransition } from '@threlte/extras' // direction is 'in', 'out' or 'both' const fly = createTransition((ref, { direction }) => { // … }) ``` ### Transition Parameters To make reusing transitions throughout your application easier, make `createTransition` the return value of a function that accepts parameters: ```ts import { isInstanceOf } from '@threlte/core' import { createTransition } from '@threlte/extras' const scale = (duration: number) => { return createTransition((ref) => { // Only apply the transition to objects if (!isInstanceOf(ref, 'Object3D')) return return { tick(t) { ref.scale.setScalar(t) }, duration } }) } ``` The transition can now be used like this: ```svelte ``` ## Transition Events Similar to Svelte transitions, Threlte transitions also emit events: ```svelte {#if visible} console.log('intro started')} onoutrostart={() => console.log('outro started')} onintroend={() => console.log('intro ended')} onoutroend={() => console.log('outro ended')} /> {/if} ``` ## Global Transitions Transitions are [local by default](https://svelte.dev/docs/svelte/transition#Local-vs-global). Local transitions only play when the block they belong to is created or destroyed, not when parent blocks are created or destroyed. Threlte offers a function `global` that marks a transition as global. ```svelte {#if x} {#if y} {/if} {/if} ``` ## TypeScript ### Prop Types By default, the `transitions` plugin does not add any prop types to the `` component. You can however extend the types of the `` component by defining the `Threlte.UserProps` type in your ambient type definitions. In a typical SvelteKit application, you can find these [in `src/app.d.ts`](https://svelte.dev/docs/kit/types#app.d.ts). The transitions plugin exports the `TransitionsProps` type which you can use as shown below: ```ts title="src/app.d.ts" {1}+ {4-6}+ import type { TransitionsProps } from '@threlte/extras' declare global { namespace Threlte { interface UserProps extends TransitionsProps {} } } export {} ``` Now all relevant properties on `` components will be type safe. ```svelte title="Scene.svelte" console.log('intro started')} /> ``` --- ## useAudioListener Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-audio-listener `useAudioListener` is a hook that either returns an existing `THREE.AudioListener` or allows a callback to immediately operate on a `THREE.AudioListener` instance in a callback passed to the hook. ### Retrieving an existing AudioListener ```ts const { listener, context } = useAudioListener() console.log(listener) // THREE.AudioListener console.log(context) // AudioContext ``` ### Using an AudioListener in a callback ```ts const filter = useAudioListener(({ listener, context }) => { return context.createBiquadFilter() }) ``` --- ## useCursor Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-cursor A hook that sets the css cursor property according to the hover state of a mesh, so that you can give the user visual feedback. If a context is present, the cursor property will be set on the DOM element of the renderer, otherwise it will be set on the body element. ## Examples ### Simple Usage Provide arguments to determine the cursor style. The defaults are`'pointer'` for `onPointerOver` and `'auto'` for `onPointerOut`. `useCursor` returns event handlers that you can use to set the hovering state: ```svelte ``` ### Renaming Event Handlers You can rename the event handlers to resolve naming conflicts. Additionally Svelte allows binding multiple event handlers to the same event: ```svelte ``` ### Store Usage If you want to implement custom logic, you can use the returned svelte store to set the hovering state: ```svelte ($hovering = true)} onpointerleave={() => ($hovering = false)} geometry={new BoxGeometry()} material={new MeshBasicMaterial()} /> ``` ### Change the Cursor Style Provide svelte stores to change the cursor style also while hovering: ```svelte ``` --- ## useFBO Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-fbo `useFBO` (Framebuffer Object) is a port of [drei's useFBO](https://github.com/pmndrs/drei#usefbo) that creates a [THREE.WebGLRenderTarget](https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderTarget). Framebuffer objects are useful when you want to render a scene to something other than the canvas. In Three.js, you do this by rendering the scene to a `RenderTarget`. The render target has a texture property that can be used for various things such as postprocessing effects or applying the texture to something in the scene. ## Options `useFBO`'s options object extends `THREE.RenderTargetOptions`. The additional properties are listed below. ### `size` `size` is used to set the `width` and `height` of the render target. If `size` is not provided, the target's size is set to the size of the canvas and it will follow any updates to the size. ```typescript const { size } = useThrelte() // use `size`'s width and height const target = useFBO() assert(target.width === size.current.width) assert(target.height === size.current.height) ``` If `size` is provided, the `width` and `height` of the texture are taken from `size`. ```typescript const size = { width: 512, height: 512 } const target = useFBO({ size }) assert(target.width === size.width) assert(target.height === size.height) ``` `width` and `height` both default to `1` if they are not found on the `size` object. ```typescript const size = { width: 512 } const target = useFBO({ size }) assert(target.width === size.width) assert(target.height === 1) ``` ```typescript const size = { height: 512 } const target = useFBO({ size }) assert(target.width === 1) assert(target.height === size.height) ``` ```typescript const size = {} const target = useFBO({ size }) assert(target.width === 1) assert(target.height === 1) ``` ### `depth` `depth` is used to assign a [DepthTexture](https://threejs.org/docs/#api/en/textures/DepthTexture) to the `depthTexture` property of the render target. It can either be a boolean, "size" object, or `DepthTexture` instance. By default, `depth` is set to `false` and a depth texture is **not** created. When `depth` is used, the scene's depth is rendered to the `depthTexture` of the render target. If `depth` is set to `true`, a depth texture is created and sized to match the size of the render target, _after_ an appropriate size has been given to the render target. ```typescript const { size } = useThrelte() const target = useFBO({ depth: true }) assert(target.depthTexture.image.width === size.current.width) assert(target.depthTexture.image.height === size.current.height) ``` If `depth` is a depth texture instance, it is assigned to the `depthTexture` property of the returned render target. ```typescript const depthTexture = new DepthTexture(512, 512) const target = useFBO({ depth: depthTexture }) assert(target.depthTexture === depthTexture) ``` If `depth` is set to a "size" object, the depth texture size is set the `width` and `height` of the object. ```typescript const depth = { width: 512, height: 512 } const target = useFBO({ depth }) assert(target.depthTexture.image.width === depth.width) assert(target.depthTexture.image.height === depth.height) ``` `width` and `height` default to `1` if they are not found on the `depth` object. ```typescript const depth = { width: 512 } const target = useFBO({ depth }) assert(target.depthTexture.image.width === depth.width) assert(target.depthTexture.image.height === 1) ``` ```typescript const depth = { height: 512 } const target = useFBO({ depth }) assert(target.depthTexture.image.width === 1) assert(target.depthTexture.image.height === depth.height) ``` ```typescript const depth = {} const target = useFBO({ depth }) assert(target.depthTexture.image.width === 1) assert(target.depthTexture.image.height === 1) ``` --- ## useFollow Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-follow `useFollow` teaches a [``](/docs/reference/extras/camera-controls) instance to track a moving target. The `` handles orbiting, collision avoidance, and damping. The hook handles the follow: it updates the controls' target to match your character (or vehicle, or any `Object3D`) each frame, and adds follow-specific behaviors like `lookAtOffset`, dead zones, and look-ahead. ```svelte ``` ## Options Because `useFollow` is a layer on ``, **camera behavior is configured on the component, follow behavior on the hook.** | Belongs on `` | Belongs on `useFollow` | | ------------------------------------- | ---------------------- | | `smoothTime` (orbit/zoom damping) | `lookAtOffset` | | `minDistance` / `maxDistance` (zoom) | `deadZone` | | `minPolarAngle` / `maxPolarAngle` | `lookAhead` | | `minAzimuthAngle` / `maxAzimuthAngle` | `followSmoothTime` | | `colliderMeshes` (collision) | `trackRotation` | | `pointerLock` (input mode) | `target` | Everything on the left is already documented on [``](/docs/reference/extras/camera-controls) — set it as a prop and `useFollow` will cooperate. ## lookAtOffset Offset added to the target's world position before it's sent to the controls. Use it to look at the character's head or chest rather than their feet: ```ts const follow = useFollow(() => ({ target, controls, lookAtOffset: [0, 1.6, 0] })) ``` ## Dead zone The target may drift this far from the currently tracked position (in camera-space right/up axes) before the camera starts to follow. Any axis left at `0` has no dead zone on that axis. ```ts const follow = useFollow(() => ({ target, controls, deadZone: [0.5, 0.3] })) ``` ## Look-Ahead Shift the tracked point in the target's direction of motion, expressed in seconds of preview. `0.3` means "place the camera where the target will be in 300 ms at its current velocity." ```ts const follow = useFollow(() => ({ target, controls, lookAhead: 0.3 })) ``` The velocity that drives `lookAhead` is itself smoothed (default time constant `0.15` s) so the offset ramps in when the character starts moving and eases out when they stop, instead of snapping. Tune it with `lookAheadSmoothTime` — lower for a snappier response, higher for a softer ease. ```ts const follow = useFollow(() => ({ target, controls, lookAhead: 0.4, lookAheadSmoothTime: 0.25 })) ``` ## followSmoothTime Seconds of lag between the character's movement and the camera's position, for a trailing / cinematic follow. `0` (the default) snaps the camera lock-step with the character. Only the tracked base position is smoothed — `lookAtOffset` and `lookAhead` are added unsmoothed on top, and the smoothed target is passed to `CameraControls` before its own update runs, so: - Adjusting `lookAtOffset` at runtime is snappy — the offset applies instantly without dragging through the smoothing window. - Collision (`colliderMeshes`), zoom limits, and other `CameraControls` features apply correctly to the smoothed pose. - User orbit/zoom input still smooths via `CameraControls`' own `smoothTime`. ```ts const follow = useFollow(() => ({ target, controls, followSmoothTime: 0.2 })) ``` ## trackRotation Drive the `CameraControls` azimuth from the target's Y-axis world rotation so the camera turns with the character. User orbit input on the horizontal axis is effectively overridden while this is on; polar orbit and zoom still work if enabled. ```ts const follow = useFollow(() => ({ target, controls, trackRotation: true, trackRotationSmoothTime: 0.25 })) ``` `trackRotationSmoothTime` tunes how quickly the camera eases into the character's heading. `0` (the default) locks the azimuth to the target's yaw every frame; higher values ramp the rotation in. `trackRotationOffset` shifts the azimuth after the target's yaw is applied. Use `Math.PI` to sit the camera behind a character whose local `+Z` axis is its forward (the common convention, e.g. when `character.rotation.y = Math.atan2(velocity.x, velocity.z)`). Use `0` for a character whose local `-Z` is forward. **Use `getTargetDirection`, not `getInputDirection`, with `trackRotation`.** Camera-relative input (`W = away from camera`) combined with rotation tracking forms a feedback loop — the character tries to face away from the camera, the camera follows the character, "away from camera" is now a new direction, and the character spins. See [getTargetDirection](#gettargetdirection). Don't combine `trackRotation` with `minAzimuthAngle`/`maxAzimuthAngle` limits on `` — the tracker will fight the limits each frame. ## getInputDirection A helper that projects a 2D input onto the camera's horizontal basis — call it from your movement task to make `WASD` / stick input always feel correct regardless of how the user has orbited. ```ts follow.getInputDirection(right, forward, out) ``` - `right` — sideways input amount (positive = to camera's right). - `forward` — forward input amount (positive = away from the camera). - `out` — a `Vector3` representing the world-space direction. Length of the vector reflects the magnitude of `(right, forward)`, so diagonal input gives a diagonal world direction. ```ts const worldMove = new Vector3() useTask((delta) => { const move = input.vector('moveLeft', 'moveRight', 'moveForward', 'moveBack') follow.getInputDirection(move.x, -move.y, worldMove) character.position.addScaledVector(worldMove, speed * delta) }) ``` `useInputMap`'s `input.vector` returns `y = -1` when "forward" is pressed, so pass `-move.y` as the forward amount. ## getTargetDirection The target-relative counterpart to `getInputDirection` — projects a 2D input onto the target's own horizontal basis (`forward` follows the target's local `+Z` axis, `right` follows its local `+X`). Use this for tank-style controls, or whenever `trackRotation` is on: ```ts follow.getTargetDirection(right, forward, out) ``` With `trackRotation`, a typical tank input loop looks like: ```ts useTask((delta) => { // A/D directly rotate the character… character.rotation.y -= input.axis('moveLeft', 'moveRight') * turnSpeed * delta // …W/S translate along the character's current forward. follow.getTargetDirection(0, -input.axis('moveForward', 'moveBack'), worldMove) character.position.addScaledVector(worldMove, speed * delta) }) ``` No yaw feedback, because the input no longer depends on the camera. ## Task ordering `useFollow` runs its update in `task`, in `mainStage`. It sets the controls target (with `followSmoothTime` smoothing baked in) and azimuth, ahead of ``' own update in the same stage. Schedule character movement **before** `task` so the camera sees the final target position for the frame. ```ts const follow = useFollow(() => ({ target, controls, lookAtOffset: [0, 1.6, 0] })) useTask( () => { // run your movement }, { before: follow.task } ) ``` ## Common patterns ### Third-person character Mouse orbit + zoom handled by ``; WASD moves the character; `useFollow` keeps the camera aimed at the character's head. Movement needs to be **camera-relative** so `W` always means "away from camera" regardless of how the user has orbited. Use [`follow.getInputDirection`](#getinputdirection) to convert the 2D input vector into a world-space direction aligned with the camera's horizontal basis. ```svelte ``` ### Top-down Lock the polar and azimuth angles on `` and the camera becomes a pure top-down follower. Orbit input still works within whatever limits you allow. ```svelte ``` ### Collision avoidance `` does a swept collision test against any meshes you hand it. Pass the obstacles as `colliderMeshes`: ```svelte {#each wallData as data, i} ... {/each} ``` No option on `useFollow` is needed — collision is entirely a `CameraControls` concern. ### Pointer lock By default `` uses click-and-drag to rotate. Flip `pointerLock` on the component to switch to mouse-look: one click locks the pointer, subsequent movement rotates the camera directly, and `Escape` releases the lock. ```svelte ``` Tune rotation speed with `pointerLockSensitivity` (radians per pixel, default `0.003`). --- ## useGamepad Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-gamepad `useGamepad` provides frame-accurate gamepad state tracking using the [Standard Gamepad layout](https://w3c.github.io/gamepad/#remapping). Button state is polled each frame, giving you `pressed`, `justPressed`, and `justReleased` on every button — just like [`useKeyboard`](/docs/reference/extras/use-keyboard). ```svelte ``` More than one gamepad can be connected at any given time, so an optional `index` can be specified. ```ts const gamepad1 = useGamepad({ index: 0 }) const gamepad2 = useGamepad({ index: 1 }) ``` ## Button State Call `gamepad.button(name)` to get the state of a button. The returned object is stable — calling `gamepad.button('clusterBottom')` always returns the same reference, so you can store it: ```ts const a = gamepad.button('clusterBottom') const lt = gamepad.button('leftTrigger') // In a useTask callback: if (a.justPressed) jump() if (lt.value > 0.5) accelerate() ``` | Property | Type | Description | | -------------- | --------- | -------------------------------------------------- | | `pressed` | `boolean` | Whether the button is currently held down | | `justPressed` | `boolean` | Whether the button was first pressed this frame | | `justReleased` | `boolean` | Whether the button was released this frame | | `touched` | `boolean` | Whether the button is being touched (if supported) | | `value` | `number` | Analog value 0–1 (e.g. triggers) | ## Stick State Call `gamepad.stick(name)` to get a stick's axis values: ```ts const left = gamepad.stick('leftStick') const right = gamepad.stick('rightStick') console.log(left.x, left.y) ``` | Property | Type | Description | | -------- | -------- | ---------------------------------- | | `x` | `number` | Horizontal axis (-1 left, 1 right) | | `y` | `number` | Vertical axis (-1 up, 1 down) | ## Reactivity Button and stick properties are reactive, so you can use them directly in your template or in `$derived` expressions: ```svelte

{a.pressed ? 'A pressed!' : 'A not pressed'}

Left stick: ({left.x.toFixed(2)}, {left.y.toFixed(2)})

``` ## Event Listeners Event listeners can be attached to individual buttons, sticks, or the gamepad itself. ```svelte ``` Available events: | Event | Description | | ------------ | ------------------------------------------------------- | | `down` | Fires when a button press begins | | `up` | Fires when a button press ends | | `press` | Fires when a button is pressed (after release) | | `change` | Fires when a button or stick value changes | | `touchstart` | Fires when a touch begins (if supported by the gamepad) | | `touchend` | Fires when a touch ends | | `touch` | Fires when a touch completes (after release) | ## Task Ordering `useGamepad` polls gamepad state in a named task (`'useGamepad'`). To ensure your game logic reads the latest state, schedule your task **after** it: ```ts useTask( () => { // gamepad state is up to date here }, { after: gamepad.task } ) ``` ## Options ### `index` The gamepad index when multiple gamepads are connected. Defaults to `0`. ### `axisDeadzone` The minimum axis value before change events fire. Defaults to `0.05`. ```ts const gamepad = useGamepad({ axisDeadzone: 0.1 }) ``` ### `mappings` A table of per-controller remap overrides. Custom entries take precedence over the built-in mappings. See [Non-Standard Controllers](#non-standard-controllers) for the full shape and examples. ### `useBuiltinMappings` When `false`, skip the built-in mapping table and use only the entries provided via `mappings`. Defaults to `true`. ## Connection Status The `connected` property is a `currentWritable` that updates when a gamepad connects or disconnects. ```svelte

{$connected ? 'Gamepad connected' : 'No gamepad'}

``` The raw unmapped [Gamepad](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad) can be accessed via `gamepad.raw`. This will be `null` unless a gamepad is connected. Gamepad data is fetched as an immutable snapshot on every frame, so any variable that caches `gamepad.raw` will become stale on the next frame. ## Button Mapping The gamepad maps 17 standard buttons and 2 sticks: **Buttons:** - **Right cluster:** `clusterBottom`, `clusterRight`, `clusterLeft`, `clusterTop` - **Bumpers and triggers:** `leftBumper`, `rightBumper`, `leftTrigger`, `rightTrigger` - **Center:** `select`, `start`, `center` - **Stick buttons:** `leftStickButton`, `rightStickButton` - **D-pad:** `directionalTop`, `directionalBottom`, `directionalLeft`, `directionalRight` **Sticks:** - `leftStick`, `rightStick` ## Non-Standard Controllers Xbox and PlayStation pads usually report `Gamepad.mapping === "standard"` and line up with the [standard gamepad layout](https://w3c.github.io/gamepad/#remapping). Many other controllers don't — most Nintendo pads report an empty mapping string and hand the browser their buttons in a different order. That's why `clusterBottom` can end up firing on the wrong press, or why buttons look stuck at rest on a pad that's sitting idle. To keep position-based names like `clusterBottom` meaning "the south face button" regardless of brand, `@threlte/extras` ships a small built-in remap table and applies it automatically to non-standard pads it recognises. Currently covered: - Nintendo Switch Pro Controller - Nintendo Switch Online SNES Controller - Nintendo Joy-Con (L) and Joy-Con (R) Non-standard pads that aren't in the table log a one-time warning with their `Gamepad.id` and read through the standard indices, which will usually be wrong. ### Adding your own mapping Pass a `mappings` object keyed by `vvvv:pppp` — the USB vendor and product IDs parsed from `Gamepad.id`, lowercase. Anything you leave unset falls back to the standard layout, so you only need to describe the inputs that differ on your hardware. ```ts const gamepad = useGamepad({ mappings: { '2dc8:3106': { buttons: { rightTrigger: { button: 15 } } } } }) ``` A mapping entry can remap buttons (`{ button: n }`, or `{ axis: n, range: 'signed' }` for triggers that come through as an axis), sticks (`{ xAxis, yAxis }` with optional `invertX` / `invertY`), and — for pads that expose the d-pad as a single hat axis — a `dpad` entry listing which axis values correspond to each direction. ## XR Gamepad For WebXR controllers with the [`xr-standard` layout](https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-heading), set `xr: true`: ```ts const left = useGamepad({ xr: true, hand: 'left' }) const right = useGamepad({ xr: true, hand: 'right' }) left.button('trigger').on('change', (event) => console.log(event)) right.button('trigger').on('change', (event) => console.log(event)) ``` **XR Buttons:** `trigger`, `squeeze`, `touchpadButton`, `thumbstickButton`, `clusterBottom`, `clusterTop` **XR Sticks:** `touchpad`, `thumbstick` --- ## useGltf Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-gltf A Hook to load glTF files and use separate object nodes and materials of it. Use the component [``](/docs/reference/extras/gltf) if you want to use a model in its entirety. Model: Battle Damaged Sci-fi Helmet by [theblueturtle\_](https://sketchfab.com/theblueturtle_) ## Examples ### Basic Example `gltf` is a store which gets populated as soon as the model loaded. ```svelte {#if $gltf} {/if} {#if $gltf} {/if} ``` ### DRACO decoding Use the `useDraco` hook for compressed glTF files, defaults to CDN loaded DRACO binaries. ```ts import { useGltf, useDraco } from '@threlte/extras' const dracoLoader = useDraco() const gltf = useGltf('/path/to/model.glb', { dracoLoader }) ``` You can set a custom path to DRACO decoder binaries. ```ts import { useGltf, useDraco } from '@threlte/extras' const dracoLoader = useDraco('/custom/draco/decoders/path') const gltf = useGltf('/path/to/model.glb', { dracoLoader }) ``` You can also provide your own instance of `DRACOLoader`. This is especially useful when you can confidently dispose of the loader, as the default loader is indefinitely cached. ```ts import { useGltf } from '@threlte/extras' const dracoLoader = new DRACOLoader().setDecoderPath(path) const gltf = useGltf('/path/to/model.glb', { dracoLoader }) ``` ### Meshopt decoding Use the `useMeshopt` hook for compressed glTF files, defaults to Three's included decoder. You can also provide your own instance of MeshoptDecoder. ```ts import { useGltf, useMeshopt } from '@threlte/extras' const meshoptDecoder = useMeshopt() const gltf = useGltf('/path/to/model.glb', { meshoptDecoder }) ``` ### KTX2Loader Use the `useKtx2` hook for [KTX 2 texture support](https://threejs.org/docs/#examples/en/loaders/KTX2Loader). This hook requires a `transcoder` path. ```ts import { useGltf, useKtx2 } from '@threlte/extras' const ktx2Loader = useKtx2('path/to/transcoder/') const gltf = useGltf('/path/to/model.glb', { ktx2Loader }) ``` You can also provide your own instance of `KTX2Loader`. This is especially useful when you can confidently dispose of the loader, as the default loader is indefinitely cached. ```ts import { useThrelte } from '@threlte/core' import { useGltf } from '@threlte/extras' const { renderer } = useThrelte() const ktx2Loader = new KTX2Loader() ktx2Loader.setTranscoderPath('path/to/transcoder/') ktx2Loader.detectSupport(renderer) const gltf = useGltf('/path/to/model.glb', { ktx2Loader }) ``` ### Nodes and Materials The hook provides a map of all objects and materials in the loaded glTF. ```svelte ``` Provide types and you will gain autocompletion for these objects and materials. ```svelte ``` On the [loading-assets](/docs/learn/basics/loading-assets) page, Threlte provides the `@threlte/gltf` CLI tool that can be used to generate a reusable Svelte component for your gltf as well as its types. Types can be separated into a typescript file and imported like so if you feel the need. ```ts title=SomeGltf.ts export type SomeGltf = { nodes: { Suzanne: THREE.Mesh } materials: {} } ``` ```svelte title=MyComponent.svelte ``` --- ## useGltfAnimations Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-gltf-animations A convenience hook to use gltf animations loaded by a [\](/docs/reference/extras/gltf) component or by the [`useGltf`](/docs/reference/extras/use-gltf) hook. Model: [Littlest Tokyo](https://artstation.com/artwork/1AGwX) by [Glen Fox](https://artstation.com/glenatron), CC Attribution. ## Examples ### With the `` component Without any arguments, `useGltfAnimations` returns a store which can be bound to the `` component. ```svelte ``` ### With the `useGltf` hook For cases where you want to reuse parts of the GLTF such as materials, nodes, or the embedded camera, `useGltfAnimations` accepts a writable store as its first argument. [`useGltf`](/docs/reference/extras/use-gltf) returns a store that can be directly passed to `useGltfAnimations`. ```svelte {#await gltf then { scene }} {/await} ``` ## Applying Animations to a Different Root `useGltfAnimations`'s second optional argument allows you to apply the animations to a root other than the GLTF scene. ```svelte {#await gltf then { scene }} {/await} ``` You can also set the root store without passing in the second argument ```svelte {#await gltf then { scene }} {/await} ``` --- ## useInputMap Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-input-map `useInputMap` provides an action mapping system that abstracts physical inputs into named actions. Define actions like `"jump"` or `"moveLeft"` once, bind them to keyboard keys and gamepad buttons, then query them by name. This decouples game logic from specific input devices. ```svelte ``` ## Reactive Definitions The definitions are passed as a function, so you can reactively update bindings at runtime — for example, when the user remaps controls in a settings screen: ```svelte ``` ## Bindings Each action maps to an array of bindings. Any active binding triggers the action. Three binding helpers are passed to the definitions callback: ### key(key) Binds a keyboard key by its [`KeyboardEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value. Matching is case-insensitive, so `'w'` matches both `'w'` and `'W'` (when Shift is held). ```ts key('Space') // spacebar key('w') // W key key('ArrowUp') // up arrow key('Shift') // either shift key ``` ### gamepadButton(button) Binds a standard gamepad button. Uses the same button names as [`useGamepad`](/docs/reference/extras/use-gamepad). ```ts gamepadButton('clusterBottom') // A / Cross gamepadButton('clusterRight') // B / Circle gamepadButton('leftTrigger') // LT / L2 gamepadButton('leftBumper') // LB / L1 ``` ### gamepadAxis(stick, axis, direction, threshold?) Binds a gamepad stick axis in a specific direction. The `threshold` parameter controls the minimum axis value before the binding activates (default `0.1`). ```ts gamepadAxis('leftStick', 'x', 1) // right on left stick gamepadAxis('leftStick', 'x', -1) // left on left stick gamepadAxis('leftStick', 'y', -1) // up on left stick (y is inverted) gamepadAxis('leftStick', 'y', 1) // down on left stick gamepadAxis('rightStick', 'x', 1, 0.2) // right on right stick, 0.2 deadzone ``` ## Action State Call `input.action(name)` to get the current state of an action: | Property | Type | Description | | -------------- | --------- | --------------------------------------------------- | | `pressed` | `boolean` | Whether any binding for this action is active | | `justPressed` | `boolean` | Whether the action became active this frame | | `justReleased` | `boolean` | Whether the action became inactive this frame | | `strength` | `number` | Analog strength 0–1. Digital inputs produce 0 or 1. | ```ts const jump = input.action('jump') if (jump.justPressed) startJump() if (jump.pressed) holdJump() if (jump.justReleased) releaseJump() ``` `strength` is useful for analog inputs like gamepad triggers, where you might want partial values. For keyboard keys, strength is always 0 or 1. Action state properties are reactive, so you can use them directly in your template: ```svelte

{input.action('sprint').pressed ? 'Sprinting!' : 'Walking'}

``` ## Axes and Vectors ### axis(negative, positive) Combines two actions into a signed axis value from -1 to 1. This is equivalent to Godot's `Input.get_axis()`. ```ts const horizontal = input.axis('moveLeft', 'moveRight') // -1 to 1 const vertical = input.axis('moveBack', 'moveForward') // -1 to 1 ``` ### vector(negativeX, positiveX, negativeY, positiveY) Combines four actions into a 2D vector, clamped to a unit circle (magnitude ≤ 1). This is equivalent to Godot's `Input.get_vector()` and handles diagonal normalization automatically. ```ts const move = input.vector('moveLeft', 'moveRight', 'moveForward', 'moveBack') // move.x: -1 to 1 // move.y: -1 to 1 // magnitude is clamped to 1 (no faster diagonal movement) ``` `vector()` returns the same object reference each call to avoid allocations in the game loop. Don't store it across frames — read the values immediately. ## Active Device `input.activeDevice.current` is a reactive property that returns `'keyboard'` or `'gamepad'`, based on whichever device most recently provided input. Can be used, for example, to show context-sensitive button prompts: ```svelte {#if input.activeDevice.current === 'keyboard'}

Press Space to jump

{:else}

Press A to jump

{/if} ``` ## Options ### keyboard A [`useKeyboard`](/docs/reference/extras/use-keyboard) instance for resolving keyboard bindings. ```ts import { useKeyboard, useInputMap } from '@threlte/extras' const keyboard = useKeyboard() const input = useInputMap( ({ key }) => ({ jump: [key('Space')] }), { keyboard } ) ``` ### gamepad Pass a [`useGamepad`](/docs/reference/extras/use-gamepad) return value to enable gamepad bindings. Only required if any action uses `gamepadButton()` or `gamepadAxis()`. ```ts import { useKeyboard, useGamepad, useInputMap } from '@threlte/extras' const keyboard = useKeyboard() const gamepad = useGamepad() const input = useInputMap( ({ key, gamepadButton }) => ({ jump: [key('Space'), gamepadButton('clusterBottom')] }), { keyboard, gamepad } ) ``` ## Task Ordering `useInputMap` runs its processing task after the internal keyboard task. Schedule your game logic after `input.task`: ```ts useTask( () => { // All action states are up to date here }, { after: input.task } ) ``` ## Keyboard-Only Example If you don't need gamepad support, just use `key()` bindings: ```svelte ``` --- ## useKeyboard Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-keyboard `useKeyboard` provides frame-accurate keyboard state tracking for game-style input. Key presses that happen between frames are collected and applied together at the start of each frame, giving you reliable `justPressed` and `justReleased` states that last exactly one frame — just like [`Input.is_action_just_pressed()`](https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-is-action-just-pressed) in Godot. ```svelte ``` ## Key State Each key is identified by its [`KeyboardEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value (e.g. `'w'`, `'Space'`, `'ArrowUp'`, `'Shift'`). Matching is case-insensitive, so `'w'` matches both `'w'` and `'W'` (when Shift is held). Call `keyboard.key(name)` to get a `KeyState` object: | Property | Type | Description | | -------------- | --------- | -------------------------------------------- | | `pressed` | `boolean` | Whether the key is currently held down | | `justPressed` | `boolean` | Whether the key was first pressed this frame | | `justReleased` | `boolean` | Whether the key was released this frame | The `KeyState` object is stable — calling `keyboard.key('Space')` always returns the same object reference, so you can store it: ```svelte ``` ## Reactivity `KeyState` properties are reactive, so you can use them directly in your template or in `$derived` expressions without a game loop: ```svelte

{space.pressed ? 'Jumping!' : 'On the ground'}

``` ## Event Listeners For immediate (non-polling) reactions, use `on()`. Events fire as soon as the browser delivers them, before frame processing. ```svelte ``` ## Task Ordering `useKeyboard` processes buffered events in a named task (`'useKeyboard'`). To ensure your game logic reads the latest keyboard state, schedule your task **after** it: ```ts useTask( () => { // keyboard state is up to date here }, { after: keyboard.task } ) ``` ## Options ### `target` The DOM element to listen on. Defaults to `window`, which receives keyboard input globally regardless of focus. Pass a specific element if you want scoped input: ```ts import { useThrelte } from '@threlte/core' const { dom } = useThrelte() const keyboard = useKeyboard(() => ({ target: dom })) ``` ### `capture` Set to `true` to listen during the capture phase. The default is `false`, so focused inputs, overlays, or other descendants can still isolate keyboard events with `stopPropagation()`. ```ts const keyboard = useKeyboard(() => ({ capture: true })) ``` ## Focus Handling When the window loses focus (blur), all pressed keys are automatically released. This prevents stuck keys when the user alt-tabs away while holding a key. --- ## useProgress Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-progress Convenience hook that wraps `THREE.DefaultLoadingManager`. Model: Battle Damaged Sci-fi Helmet by [theblueturtle\_](https://sketchfab.com/theblueturtle_) ### Examples #### Basic Example You can use and place this hook anywhere. Typically you would use this hook outside of your `` component to show a loading indicator in your DOM. ```svelte ``` --- ## useSuspense Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-suspense The hook `useSuspense` is used to mark a resource as being used in a `` boundary or to get the `suspended`-state of the closest `` boundary. For a complete implementation example, have a look at the [`` component](/docs/reference/extras/suspense). ## Usage ### Suspend an Async Resource The hook returns a function that allows you to mark a resource as being used in a `` boundary. To suspend the closest `` boundary, call the function returned by `useSuspense()` and pass a promise as the first argument. Because [`useLoader().load()`](/docs/reference/core/use-loader) returns an [`AsyncWritable`](/docs/reference/core/utilities#asyncwritable), the result of `useLoader().load()` can be passed directly to the function returned by `useSuspense()`. ```svelte ``` ### Get Suspended State The hook can be used to get the `suspended`-state of the closest `` boundary. ```svelte ``` --- ## useTexture Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-texture `useTexture` is a convenience hook wrapping [`useLoader`](/docs/reference/core/hooks#useloader) that returns an [`AsyncWritable`](/docs/reference/core/utilities#asyncwritable) store that is eventually populated with a `THREE.Texture`. The texture is automatically assigned the [`colorSpace`](https://threejs.org/docs/#api/en/textures/Texture.colorSpace) that the renderer uses. ## Usage ### Basic Example ```svelte {#await texture then map} {/await} ``` ### Transforming the Texture You can pass a `transform` function to transform the texture **once** its loaded. ```svelte {#await texture then map} {/await} ``` Be aware that the transformed result will be cached for subsequent calls to `useTexture` with the same URL. --- ## useThrelteAudio Package: @threlte/extras URL: https://threlte.xyz/docs/reference/extras/use-threlte-audio When invoking the hook `useThrelteAudio` and there's no Threlte audio context yet, it will be created and returned, otherwise, the existing audio context will be returned. The components [``](/docs/reference/extras/audio-listener), [`