A three.js component library for svelte.
Index
- What is threlte?
- Getting started
- Concepts
- Conventions
- Reference
- Credits
- Thank you
- License
What is threlte?
Threlte is a component library for svelte to build and render three.js scenes declaratively and state-driven in Svelte apps.
It's inspired by the sensible defaults of react-three-fiber, the simplicity and effectiveness of Sveltes reactivity model and Svelte Cubed.
It provides strictly typed components to quickly and easily build three.js scenes with deep reactivity and interactivity out-of-the-box.
It also aims to provide the building blocks to quickly extend threlte when it's needed.
Getting started
Install threlte and three.js:
npm install threlte three
For TypeScript users, install three.js types:
npm install -D @types/three
Build your first scene:
<script>
import {
CircleBufferGeometry,
MeshStandardMaterial,
BoxBufferGeometry,
DoubleSide
} from "three";
import {
Canvas,
DirectionalLight,
HemisphereLight,
Mesh,
OrbitControls,
PerspectiveCamera
} from "threlte";
</script>
<div>
<Canvas>
<PerspectiveCamera position={{ x: 10, y: 10, z: 10 }}>
<OrbitControls autoRotate />
</PerspectiveCamera>
<DirectionalLight shadow color={'white'} position={{ x: -15, y: 45, z: 20 }} />
<HemisphereLight skyColor={'white'} groundColor={'#ac844c'} intensity={0.4} />
<Mesh
castShadow
geometry={new BoxBufferGeometry(1, 1, 1)}
material={new MeshStandardMaterial({ color: '#ff3e00' })}
/>
<Mesh
receiveShadow
position={{ y: -0.5 }}
rotation={{ x: -90 * (Math.PI / 180) }}
geometry={new CircleBufferGeometry(3, 72)}
material={new MeshStandardMaterial({ side: DoubleSide, color: 'white' })}
/>
</Canvas>
</div>
<style>
div {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
It should look something like this:
Congratulations
Orbit around the cube and have fun using threlte.
Check out the slightly advanced example scene including interactivity on CodeSandbox.
Concepts
Yes, there are already three.js component libraries for svelte, threlte is different in some ways:
- Sensible defaults
Much like react-three-fiber, threlte will set sensible defaults to three.jsWebGLRenderer
, all colors and textures and more. This makes it easy for you to follow best practices in terms of color reception and accuracy.
threlte also makes visibility management a breeze with its<Layers>
component. - Unified frame loop
By default, threlte only renders the scene if there's need for it: If a prop changes that makes rendering the scene necessary, if there are any interactive objects in the scene or if threlte or you useuseFrame
in any of your components. - Interactivity
threlte makes it possible to use events on three.js objects as if they were regular DOM elements:
<Mesh … interactive on:click={onClick}>
You can also listen to your object leaving or entering the viewport:
<Mesh … viewportAware on:viewportenter={onViewportEnter}>
- TypeScript
All threlte components are written in TypeScript, so type support is a first-class citizen. - EffectComposer support
Add a Pass with
<Pass pass={new GlitchPass()} />
and threlte will take care of setting up the defaultRenderPass
and render to theEffectComposer
instead of theWebGLRenderer
. - Text rendering
Render text using the fantastic troika-three-text library with:<Text text="Hello World" />
- Access All Areas
- Bind to three.js object instances
<Mesh … bind:mesh>
- Access the renderer
const { renderer, invalidate } = useThrelte()
- Bind to three.js object instances
- Easily extendable
Build objects that didn't yet make it to threlte yourself by plugging together functional components. - Tree-shakeble
react-three-fiber is great at making it possible to use three.js classes as JSX components. This means that there is no hard dependency on a certain three.js version and everything that is possible in three.js is covered with react-three-fiber as well. There is however a downside: react-three-fiber looks up three.js classes at runtime. This means that even if your react-three-fiber app only uses a fraction of three.js, you will need to ship three.js in its entirety.
threlte does not look up three.js classes at runtime and as such is limited in features compared to three.js itself. It tries however to cover most use cases of three.js and provides functional components to make extending threlte as easy as possible. As such, your bundler is able to tree-shake threlte and limit what parts of three.js get shipped. - Does not get in your way
Everything is accessible. Objects are instantiated without any default values, so that threlte will not get in your way of setting up or altering objects manually while also relying on the defaults set by three.js.
Interactivity
Open the interactivity example in CodeSandbox
Listen to events of a <Mesh>
and a <MeshInstance>
as if it would be a regular DOM element:
<Mesh … interactive on:click={onClick} />
These events are supported:
click
contextmenu
pointerup
pointerdown
pointerenter
pointerleave
pointermove
All events include the raycast Intersection object:
<script lang="ts">
import { Mesh, ThreltePointerEvent } from 'threlte'
const onClick = (e: CustomEvent<ThreltePointerEvent>) => {
const distanceToMesh = e.detail.distance
}
</script>
<Mesh … interactive on:click={onClick}>
⚠️ You must addinteractive
to your component to be able to listen to pointer events
Be aware that this will make the frameloop render on every frame.
Viewport Awareness
Open the viewport awareness example in CodeSandbox
Additionally, most Objects (Lights, Cameras, Meshes, …) can be made viewport aware. Use it to lazily load textures, models and more.
<script lang="ts">
import { PointLight } from 'threlte'
import type { Object3D } from 'three'
let inViewport
const onViewportEnter = (e: CustomEvent<Object3D>) => {
console.log('PointLight entered the viewport.')
}
const onViewportLeave = (e: CustomEvent<Object3D>) => {
console.log('PointLight left the viewport.')
}
</script>
<PointLight
viewportAware
bind:inViewport
on:viewportenter={onViewportEnter}
on:viewportleave={onViewportLeave}
/>
These events are supported:
viewportenter
viewportleave
Bind inViewport
if you wish to not use events.
⚠️ You must addviewportAware
to your component to make it viewport aware
Reactivity
Open the reactivity example in CodeSandbox
Just like Svelte Cubed and much unlike react-three-fiber it is encouraged to use your component state to drive your three.js scene. By using props instead of manipulating three.js objects directly, the unified render loop is able to tell that your scene needs rerendering and svelte can make use of component optimizations.
Conventions
Threlte components follow the principles of three.js whereever possible and useful in terms of available properties and events and their respective naming.
Most component properties are undefined
by default. Therefore, new three.js objects are instantiated without any optional default values. This way, three.js will provide the defaults and threltes reactivity will not kick in, allowing you to take complete control over objects.
On top of that, threlte adds some functionality to make objects even more reactive.
lookAt
Use the property lookAt
on an Object to
- reactively orient an Object3D towards another Object3D
<script>
let mesh
</script>
<PerspectiveCamera lookAt={mesh} />
<Mesh bind:mesh … />
- to reactively orient an Object towards a Position
<PerspectiveCamera lookAt={{ x: 5, y: 3 }} />
Reference
Types
TypeScript users should install @types/three in order to get type support for three.js.
npm install -D @types/three
Property Types
To make working with component props easier, threlte includes special types for position, scale, rotation and lookAt:
type Position = THREE.Vector3 | { x?: number, y?: number, z?: number }
const positionA = new Vector3()
const positionB = { x: 1, z: 1 }
type Scale = THREE.Vector3 | number | { x?: number, y?: number, z?: number }
const scaleA = new Vector3()
const scaleB = 2
const scaleC = { x: 1.5, z: 1 }
type Rotation = THREE.Euler | { x?: number, y?: number, z?: number, order?: THREE.Euler['order'] }
const rotationA = new Euler()
const rotationB = { x: 1.5, z: 1 }
type LookAt = THREE.Vector3 | { x?: number, y?: number, z?: number } | THREE.Object3D
const lookAtA = new Vector3()
const lookAtB = { x: 2, y: 3 }
const lookAtC = someMesh
Context Types
The <Canvas>
component provides two very useful contexts: ThrelteContext
and ThrelteRootContext
type ThrelteContext = {
size: { width: number; height: number }
pointer?: Vector2
clock: Clock
camera?: Camera
scene: Scene
renderer?: WebGLRenderer
composer?: EffectComposer
invalidate: (reason?: string) => void
}
type ThrelteRootContext = {
setCamera: (camera: Camera) => void
linear: boolean
resizeOptions?: UseResizeOptions
addPass: (pass: Pass) => void
removePass: (pass: Pass) => void
addRaycastableObject: (obj: Object3D) => void
removeRaycastableObject: (obj: Object3D) => void
addInteractiveObject: (obj: Object3D) => void
removeInteractiveObject: (obj: Object3D) => void
interactiveObjects: Set<Object3D>
raycastableObjects: Set<Object3D>
raycaster: Raycaster
lastIntersection: Intersection<Object3D<Event>> | null
}
See useThrelte
and useThrelteRoot
on how to use these.
type UseResizeOptions = {
axis?: 'horizontal' | 'vertical' | 'both'
runOnInit?: boolean
debounce?: number
}
Components
Type information for threlte component properties, bindings and events are detailed below in the following form:
// optional
name: type = default
// required
name: type
📋
<Canvas>
The <Canvas>
component is the root of your three.js scene.
By default, the <canvas>
element and the renderer will resize to fit the parent element whenever the window resizes.
Properties
// optional
dpr: number = browser ? window.devicePixelRatio : 1
flat: boolean = false
linear: boolean = false
frameloop: 'always' | 'demand' = 'demand'
debugFrameloop: boolean = false
shadows: boolean = true
shadowMapType: THREE.ShadowMapType = THREE.PCFSoftShadowMap
resizeOptions: {
axis?: 'horizontal' | 'vertical' | 'both'
runOnInit?: boolean
debounce?: number
} | undefined = undefined
Bindings
ctx: ThrelteContext
rootCtx: ThrelteRootContext
See Context Types for details
🌐
Objects
🌐
<Mesh>
Example
<script>
import { Mesh } from 'threlte'
import { BoxBufferGeometry, MeshBasicMaterial } from 'three'
</script>
<Mesh
position={{ y: 1 }}
geometry={new BoxBufferGeometry(1, 2, 1)}
material={new MeshBasicMaterial({ wireframe: true })}
/>
Properties
// required
geometry: THREE.BufferGeometry
material: THREE.Material | THREE.Material[]
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
interactive: boolean = false
ignorePointerEvents: boolean = false
Bindings
inViewport: boolean
mesh: THREE.Mesh
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
click: CustomEvent<ThreltePointerEvent>
contextmenu: CustomEvent<ThreltePointerEvent>
pointerup: CustomEvent<ThreltePointerEvent>
pointerdown: CustomEvent<ThreltePointerEvent>
pointerenter: CustomEvent<ThreltePointerEvent>
pointerleave: CustomEvent<ThreltePointerEvent>
pointermove: CustomEvent<ThreltePointerEvent>
🌐
<Group>
Example
<script>
import { Group, GLTF } from 'threlte'
</script>
<Group position={{ x: 5 }}>
<GLTF url={"/models/modelA.glb"} />
<GLTF url={"/models/modelB.glb"} />
</Group>
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
Bindings
inViewport: boolean
group: THREE.Group
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🌐
<Object3D>
Example
You might want to use this component to pass as a reference to other components:
<script>
import { Object3D, PerspectiveCamera } from 'threlte'
let target
</script>
<PerspectiveCamera lookAt={target} />
<Object3D bind:object={target} />
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
Bindings
inViewport: boolean
object: THREE.Object3D
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🌐
<GLTF>
To use DRACO compression, provide a path to the DRACO decoder.
To use KTX2 compressed textures, provide a path to the KTX2 transcoder.
Example
<GLTF
castShadow
receiveShadow
url={'/models/flower.glb'}
position={{ y: 1 }}
scale={3}
/>
Properties
// required
url: string
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
dracoDecoderPath: string | undefined = undefined
ktxTranscoderPath: string | undefined = undefined
Bindings
gltf: GLTF
scene: THREE.Group
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
♻️
Object Instances
While object components like <Mesh>
create a new object for you (in the case of <Mesh>
it's a THREE.Mesh
), an object instance component takes an existing object instance (THREE.Mesh
, THREE.Object3D
, THREE.Light
or THREE.Camera
) as a property and applies reactivity to it. It's used internally but can also be used to introduce reactivity to objects that need to be instanced manually, imported models or objects that did not yet make it into threlte.
Object instance components intentionally have no default values on properties even if they can be undefined
. That way, your IDE will tell you what properties need to be implemented to properly set them up.
Example
<script>
import { Object3D } from 'three'
import { Object3DInstance, useFrame } from 'threlte'
const object = new Object3D()
let position = { x: 1, y: 1, z: 1 }
useFrame(() => {
position.x += 0.1
})
</script>
<Object3DInstance {object} {position} />
♻️
<Object3DInstance>
Example
<script>
import { Object3D } from 'three'
import { Object3DInstance } from 'threlte'
const object = new Object3D()
</script>
<Object3DInstance {object} position={{ y: 1 }} />
Properties
// required
object: THREE.Object3D
viewportAware: boolean
// optional
position: Position | undefined
scale: Scale | undefined
rotation: Rotation | undefined
lookAt: LookAt | undefined
castShadow: boolean | undefined
receiveShadow: boolean | undefined
frustumCulled: boolean | undefined
renderOrder: number | undefined
Bindings
inViewport: boolean
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
♻️
<MeshInstance>
Example
<script>
import { Mesh, MeshStandardMaterial, BoxBufferGeometry } from 'three'
import { MeshInstance } from 'threlte'
const material = new MeshStandardMaterial({
wireframe: true,
color: 'black'
})
const geometry = new BoxBufferGeometry(1, 1, 1)
const mesh = new Mesh(geometry, material)
</script>
<MeshInstance {mesh} rotation={{ x: 90 * (Math.PI / 180) }} />
Properties
// required
mesh: THREE.Mesh
interactive: boolean
ignorePointerEvents: boolean
viewportAware: boolean
// optional
position: Position | undefined
scale: Scale | undefined
rotation: Rotation | undefined
lookAt: LookAt | undefined
castShadow: boolean | undefined
receiveShadow: boolean | undefined
frustumCulled: boolean | undefined
renderOrder: number | undefined
Bindings
inViewport: boolean
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
click: CustomEvent<ThreltePointerEvent>
contextmenu: CustomEvent<ThreltePointerEvent>
pointerup: CustomEvent<ThreltePointerEvent>
pointerdown: CustomEvent<ThreltePointerEvent>
pointerenter: CustomEvent<ThreltePointerEvent>
pointerleave: CustomEvent<ThreltePointerEvent>
pointermove: CustomEvent<ThreltePointerEvent>
♻️
<CameraInstance>
Example
<script>
import { PerspectiveCamera } from 'three'
import { CameraInstance } from 'threlte'
const camera = new PerspectiveCamera(45, 1, 1, 1000)
</script>
<CameraInstance useCamera={false} {camera} />
Properties
// required
camera: THREE.Camera
viewportAware: boolean
useCamera: boolean
// optional
position: Position | undefined
scale: Scale | undefined
rotation: Rotation | undefined
lookAt: LookAt | undefined
castShadow: boolean | undefined
receiveShadow: boolean | undefined
frustumCulled: boolean | undefined
renderOrder: number | undefined
Bindings
inViewport: boolean
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
♻️
<LightInstance>
Example
<script>
import { RectAreaLight } from 'three'
import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib'
import { LightInstance } from 'threlte'
RectAreaLightUniformsLib.init()
export let intensity
const light = new RectAreaLight(0xffffff, intensity, 10, 10)
</script>
<LightInstance {light} {intensity} position={{ x: 5, y: 5 }} />
Properties
// required
light: THREE.Light
viewportAware: boolean
// optional
position: Position | undefined
scale: Scale | undefined
rotation: Rotation | undefined
lookAt: LookAt | undefined
castShadow: boolean | undefined
receiveShadow: boolean | undefined
frustumCulled: boolean | undefined
renderOrder: number | undefined
color: THREE.ColorRepresentation | undefined
intensity: number | undefined
Bindings
inViewport: boolean
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🔆
Lights
🔆
<AmbientLight>
Example
<script>
import { AmbientLight } from 'threlte'
</script>
<AmbientLight color={0xd7681c} intensity={0.3} />
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
color: THREE.ColorRepresentation | undefined = undefined
intensity: number | undefined = undefined
Bindings
inViewport: boolean
light: THREE.AmbientLight
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🔆
<DirectionalLight>
Example
<script>
import { DirectionalLight } from 'threlte'
</script>
<DirectionalLight
shadow
intensity={0.6}
position={{ x: 3, y: 10, z: 3 }}
/>
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
color: THREE.ColorRepresentation | undefined = undefined
intensity: number | undefined = undefined
shadow:
| boolean
| {
mapSize?: [number, number] | undefined
camera?:
| {
left?: number | undefined
right?: number | undefined
top?: number | undefined
bottom?: number | undefined
near?: number | undefined
far?: number | undefined
}
| undefined
bias?: number | undefined
radius?: number | undefined
}
| undefined = undefined
Bindings
inViewport: boolean
light: THREE.DirectionalLight
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🔆
<HemisphereLight>
Example
<script>
import { HemisphereLight } from 'threlte'
</script>
<HemisphereLight
skyColor={0xFB9796}
groundColor={0xC1B8A5}
/>
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
receiveShadow: boolean | undefined = undefined
castShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
intensity: number | undefined = undefined
skyColor: THREE.ColorRepresentation | undefined = undefined
groundColor: THREE.ColorRepresentation | undefined = undefined
Bindings
inViewport: boolean
light: THREE.HemisphereLight
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🔆
<PointLight>
Example
<script>
import { PointLight } from 'threlte'
</script>
<PointLight position={{ x: 3, y: 3 }} />
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
color: THREE.ColorRepresentation | undefined = undefined
intensity: number | undefined = undefined
distance: number | undefined = undefined
decay: number | undefined = undefined
power: number | undefined = undefined
shadow:
| boolean
| {
mapSize?: [number, number]
camera?: { near?: number; far?: number }
bias?: number
radius?: number
}
| undefined = undefined
Bindings
inViewport: boolean
light: THREE.PointLight
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🔆
<SpotLight>
Example
<script>
import { SpotLight, Mesh } from 'threlte'
let mesh
</script>
<Mesh … bind:mesh />
<SpotLight position={{ x: 3, y: 3 }} target={mesh} />
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
color: THREE.ColorRepresentation | undefined = undefined
intensity: number | undefined = undefined
angle: number | undefined = undefined
decay: number | undefined = undefined
distance: number | undefined = undefined
penumbra: number | undefined = undefined
power: number | undefined = undefined
target: LookAt | undefined = undefined
shadow:
| boolean
| {
mapSize?: [number, number]
camera?: { near?: number; far?: number }
bias?: number
radius?: number
}
| undefined = undefined
Bindings
inViewport: boolean
light: THREE.SpotLight
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🎥
Cameras
🎥
<OrthographicCamera>
Example
<script>
import { OrthographicCamera } from 'threlte'
</script>
<OrthographicCamera
position={{ x: 3, y: 3, z: 3 }}
lookAt={{ x: 0, y: 0, z: 0 }}
/>
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
useCamera: boolean = true
near: number = undefined
far: number = undefined
zoom: number = undefined
Bindings
inViewport: boolean
camera: THREE.OrthographicCamera
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🎥
<PerspectiveCamera>
Example
<script>
import { PerspectiveCamera } from 'threlte'
</script>
<PerspectiveCamera
fov={60}
position={{ x: 3, y: 3, z: 3 }}
lookAt={{ x: 0, y: 0, z: 0 }}
/>
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
useCamera: boolean = true
near: number = undefined
far: number = undefined
fov: number = undefined
Bindings
inViewport: boolean
camera: THREE.PerspectiveCamera
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
🔁
Controls
🔁
<OrbitControls>
The component <OrbitControls>
must be a direct child of a camera component and will mount itself to that camera. As soon as the OrbitControls are mounted, the frame loop will continously run.
Example
<script>
import { PerspectiveCamera, OrbitControls } from 'threlte'
</script>
<PerspectiveCamera fov={50}>
<OrbitControls enableDamping />
</PerspectiveCamera>
Properties
// optional
autoRotate: boolean | undefined = undefined
autoRotateSpeed: number | undefined = undefined
dampingFactor: number | undefined = undefined
enableDamping: boolean | undefined = undefined
enabled: boolean | undefined = undefined
enablePan: boolean | undefined = undefined
enableRotate: boolean | undefined = undefined
enableZoom: boolean | undefined = undefined
keyPanSpeed: number | undefined = undefined
keys:
| {
LEFT: string
UP: string
RIGHT: string
BOTTOM: string
}
| undefined = undefined
maxAzimuthAngle: number | undefined = undefined
maxDistance: number | undefined = undefined
maxPolarAngle: number | undefined = undefined
maxZoom: number | undefined = undefined
minAzimuthAngle: number | undefined = undefined
minDistance: number | undefined = undefined
minPolarAngle: number | undefined = undefined
minZoom: number | undefined = undefined
mouseButtons:
| {
LEFT: MOUSE
MIDDLE: MOUSE
RIGHT: MOUSE
}
| undefined = undefined
panSpeed: number | undefined = undefined
rotateSpeed: number | undefined = undefined
screenSpacePanning: boolean | undefined = undefined
touches:
| {
ONE: TOUCH
TWO: TOUCH
}
| undefined = undefined
zoomSpeed: number | undefined = undefined
target: Position | undefined = undefined
Bindings
controls: THREE.OrbitControls
Events
change: CustomEvent<undefined>
start: CustomEvent<undefined>
end: CustomEvent<undefined>
💄
Post Processing
💄
<Pass>
By default, threlte will render using the regular WebGLRenderer. If any Pass is added to the scene, the EffectComposer
will take over the rendering. A default RenderPass
is added automatically and rendered before any <Pass>
.
Example
<script>
import { Pass } from 'threlte'
import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass'
</scrip>
<Pass pass={new GlitchPass()}>
Properties
// required
pass: THREE.Pass
Misc
💭
<Fog>
A <Fog>
adds itself to the scene directly. The placement in the hierarchy is therefore unimportant as long as it's inside the <Canvas>
component.
Example
<script>
import { Fog } from 'threlte'
</script>
<Fog color={"#dddddd"} />
Properties
// optional
color: THREE.ColorRepresentation = 0xffffff
near: number | undefined = undefined
far: number | undefined = undefined
Bindings
fog: THREE.Fog
💭
<FogExp2>
A <FogExp2>
adds itself to the scene directly. The placement in the hierarchy is therefore unimportant as long as it's inside the <Canvas>
component.
Example
<script>
import { FogExp2 } from 'threlte'
</script>
<FogExp2 color={"#dddddd"} density={0.3} />
Properties
// optional
color: THREE.ColorRepresentation = 0xffffff
density: number | undefined = undefined
Bindings
fog: THREE.FogExp2
🔤
<Text>
The <Text>
component uses troika-three-text to render text.
Example
<script>
import { Text } from 'threlte'
let value = ''
</script>
<input type="text" bind:value />
<Text text={value} />
Properties
// optional
position: Position | undefined = undefined
scale: Scale | undefined = undefined
rotation: Rotation | undefined = undefined
lookAt: LookAt | undefined = undefined
viewportAware: boolean = false
castShadow: boolean | undefined = undefined
receiveShadow: boolean | undefined = undefined
frustumCulled: boolean | undefined = undefined
renderOrder: number | undefined = undefined
interactive: boolean = false
ignorePointerEvents: boolean = false
text: string | undefined = undefined
anchorX: number | 'left' | 'center' | 'right' | string | undefined = undefined
anchorY: number | 'top' | 'top-baseline' | 'middle' | 'bottom-baseline' | 'bottom' | string | undefined = undefined
curveRadius: number | undefined = undefined
font: null | string | undefined = undefined
fontSize: number | undefined = undefined
letterSpacing: number | undefined = undefined
lineHeight: number | string | undefined = undefined
maxWidth: number | undefined = undefined
overflowWrap: 'normal' | 'break-word' | 'normal' | undefined = undefined
textAlign: 'left' | 'right' | 'center' | 'justify' | undefined = undefined
textIndent: number | undefined = undefined
whiteSpace: 'normal' | 'nowrap' | 'pre-wrap' | undefined = undefined
material: THREE.Material | THREE.Material[] | null | undefined = undefined
color: string | number | THREE.Color | null | undefined = undefined
depthOffset: number | undefined = undefined
clipRect: [number, number, number, number] | null | undefined = undefined
glyphGeometryDetail: number | undefined = undefined
sdfGlyphSize: number | null | undefined = undefined
outlineWidth: number | string | undefined = undefined
outlineColor: THREE.ColorRepresentation | undefined = undefined
outlineOpacity: number | undefined = undefined
outlineBlur: number | string | undefined = undefined
outlineOffsetX: number | string | undefined = undefined
outlineOffsetY: number | string | undefined = undefined
strokeWidth: number | string | undefined = undefined
strokeColor: THREE.ColorRepresentation | THREE.Color | undefined = undefined
strokeOpacity: number | undefined = undefined
fillOpacity: number | undefined = undefined
Bindings
inViewport: boolean
text: Text
Events
viewportenter: CustomEvent<undefined>
viewportleave: CustomEvent<undefined>
click: CustomEvent<ThreltePointerEvent>
contextmenu: CustomEvent<ThreltePointerEvent>
pointerup: CustomEvent<ThreltePointerEvent>
pointerdown: CustomEvent<ThreltePointerEvent>
pointerenter: CustomEvent<ThreltePointerEvent>
pointerleave: CustomEvent<ThreltePointerEvent>
pointermove: CustomEvent<ThreltePointerEvent>
🔲
<Layers>
Layers are one of many ways to manage the visibility of objects in three.js.
The <Layers>
component assigns all child components the layer memberships you pass to it. Any object that is a member of the same layers the camera is on, is visible.
Example
<script>
import { Layers, PerspectiveCamera, Mesh } from 'threlte'
</script>
<!-- Remember to also assign a layer to your camera -->
<Layers layers={1}>
<PerspectiveCamera />
</Layers>
<!-- visible if camera is on any layer -->
<Layers layers={'all'}>
<Mesh … />
</Layers>
<!-- visible if camera is on layer 1 -->
<Layers layers={1}>
<Mesh … />
</Layers>
<!-- visible if camera is on layer 0 or 1 -->
<Layers layers={[0, 1]}>
<Mesh … />
</Layers>
<!-- invisible -->
<Layers layers={'none'}>
<Mesh … />
</Layers>
Properties
// required
layers: ThrelteLayers
Property layers
can be:
- any integer from 0 to 31
- an array of any integer from 0 to 31
'all'
'none'
TypeScript users will benefit from strong types, JavaScript users should be aware that there is no runtime validation happening.
↩️
Hooks
⚠️ Hooks can only be used inside the<Canvas>
component because they rely on context⚠️
↩️
useThrelte
This hook lets you consume the state of the <Canvas>
component which contains the renderer, camera, scene and so on.
const {
size, // { width: number; height: number }
pointer, // Vector2 | undefined
clock, // Clock
camera, // Camera | undefined
scene, // Scene
renderer, // WebGLRenderer | undefined
composer, // EffectComposer | undefined
invalidate, // (reason?: string) => void
} = useThrelte()
If your frame loop is set to 'demand'
and you are manually editing objects or materials, be sure to invalidate the current frame to request a rerender:
const { invalidate } = useThrelte()
invalidate()
// Optionally provide a reason to debug the frame loop
invalidate('changed material color')
This context is also available as the first argument of the callback of the useFrame hook!
↩️
useThrelteRoot
This hook lets you consume the root context. Although it can be useful, this is mostly used internally.
const {
setCamera, // : (camera: Camera) => void
linear, // : boolean
resizeOptions, // ?: UseResizeOptions
addPass, // : (pass: Pass) => void
removePass, // : (pass: Pass) => void
addRaycastableObject, // : (obj: Object3D) => void
removeRaycastableObject, // : (obj: Object3D) => void
addInteractiveObject, // : (obj: Object3D) => void
removeInteractiveObject, // : (obj: Object3D) => void
interactiveObjects, // : Set<Object3D>
raycastableObjects, // : Set<Object3D>
raycaster, // : Raycaster
lastIntersection, // : Intersection<Object3D<Event>> | null
} = useThrelteRoot()
↩️
useFrame
This hook allows you to execute code on every frame.
You receive the state (the same as useThrelte
) and a clock delta in seconds. Your callback function will be invoked just before a frame is rendered. When the component unmounts it is unsubscribed automatically from the frame loop. You may pass additional options to this hook. Also, useFrame
returns an object containing functions start
and stop
to control the execution of the callback and a store started
to subscribe to its state.
Example
const { start, stop, started } = useFrame(() => {
console.log('rendering…')
})
const toggleUseFrame = () => {
if ($started) {
stop()
} else {
start()
}
}
↩️
useLoader
useLoader
memoizes Loaders to reuse them at any time. Pass in the Loaders class and a function to instantiate the loader.
Example
import { useLoader } from 'threlte'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
const loader = useLoader(OBJLoader, () => new OBJLoader())
loader.load('models/model.obj', (obj) => {
console.log(object)
})
↩️
useTexture
useTexture
allows you to easily load textures and automatically convert your textures to the correct color space based on your settings on the <Canvas>
component. This hook can be called outside of the <Canvas>
component, but with limitations: To make sure that textures are converted to the correct color space, useTexture
needs to consume the <Canvas>
context. If there's no context, it will fall back to keeping your textures untouched.
Examples
const tex = useTexture('tex.jpg')
// THREE.Texture
const [texA, texB] = useTexture(['texA.jpg', 'texB.jpg'])
// THREE.Texture[]
const textures = useTexture({ bumpMap: 'bump.jpg', map: 'map.jpg' })
// { bumpMap: THREE.Texture, map: THREE.Texture }
const material = new MeshStandardMaterial({ ...textures })
Credits
Thank you
- Rich Harris for the incredible work on Svelte, SvelteKit and Svelte Cubed
- @larissamantel for the logo
License
The MIT License (MIT). Please see License File for more information.