Introduction to Threlte
Hey friends, today I'm going to show you how you can create interactive 3D experiences for the web using Threlte. So if you don't know, Threlte recently released their eighth version. So it's been around for a while. But what is actually Threlte? Well, Threlte is a 3D framework for the web built on top of Svelte and Three.js. So today I'm going to show you how you can get started with Threlte. I'm going to show you how you can convert Three.js concepts to Threlte, etc., because I think that's the most important part. And if you aren't aware, Three.js is a popular JavaScript library for building 3D on the web. So you can build anything from cool interactions to games.
So let me show you an example of Three.js. So this is a basic Three.js example using Svelte. So here we're just using a Svelte action useScene. We're importing the namespace three from Three.js. So we're creating just a scene here and a camera, a sphere, and then we're using the renderer to output it to the canvas. Basically, it's really not that intimidating as you can see. But let's look at the equivalent Three.js code. So we're taking this imperative JavaScript code where you have to define line by line what it has to do. So we're using Svelte to make it more declarative.
And here's the same example using Threlte. You can already see how much cleaner it looks. And don't worry, we're going to cover everything step by step. But if you're newer to Svelte or Three.js, I highly encourage you to go through the Svelte tutorial and read the Three.js fundamentals. So you can learn a lot here from how the renderer works to how the camera works and etc. So you can see this is a really great resource and it's really not that long to read. Or of course, you can watch a video or anything else on that subject. All of the resources that I mentioned in this video are going to be linked in the description.
Threlte Basics: Building a Scene
Let's go through some Threlte basics. In this example I have an App.svelte component. So the first thing I'm going to do is import Canvas from @threlte/core. The canvas component is the root of a Threlte app. It creates a renderer and sets up some sensible defaults like anti-aliasing and color management and gives you a default camera.
The next thing I'm going to do is import a scene from Scene.svelte. And this is mostly because Threlte uses Svelte's context API. So it sets the context in the canvas component and then it needs to access it inside of children components. And this is something that always gets me. So don't forget to also add a div wrapper around it. And you can set the height and width to 100% if you want. Otherwise, you're not going to see anything. The canvas is going to take the entire width and height of the parent. All right, but this is boring. So, let's actually add something to our scene.
First, I'm going to import T from @threlte/core. And as you can see here, we're adding a mesh and a sphere geometry which we can pass some args to. And this is equivalent of passing arguments to a constructor because under the hood, this is just a Three.js class. So, here we're passing 2 as a radius and 32 as the width and height segments.
Next, we're going to add a simple mesh using the standard material, which requires a light source, and that is why everything is black and we don't see anything. So, let there be light. And as you can see, we're adding a simple directional light. And we're setting the X, Y, and Z position. I've also increased the intensity to two, so it's brighter. And I'm going to say castShadow. So, we're going to see the shadow on our sphere.
And as you can see, this looks a bit rough, but we can actually do a simple trick, and that is add an ambient light. And this basically lights everything evenly in your scene. It's basically like cheap global illumination.
Next, I'm going to add the plane. But as you can see, we can't actually see it because it's looking directly at us. And I'm just rotating it 90 degrees using Math.PI / 2 with some arguments for the width and height with a basic standard material. All right. So, let's add a camera. And we can add a new perspective camera. We have to say makeDefault so it uses this camera instead. And then we can set the position. And we can even pass a on:create prop. We get a reference to the actual mesh. So we can say, hey, wherever you are in this space, look at this mesh at coordinates 0, 0, 0.
Adding Interactivity and Animation
Let's look at how we can add some interactivity. We can import the interactivity plugin from @threlte/extras. And then we can use spring from Svelte to animate this value over time. And spring is basically just a signal under the hood. And on the mesh, we can set the scale to the signal value. So we have to say scale.set(). And we can use this event listeners on:pointerenter and on:pointerleave. So we can set the scale to 0.5 when the pointer enters the sphere and we can set it back to one when the pointer leaves the sphere.
And we can also rotate the sphere. We can just set rotation and then we can use this useTask hook. So we can use this to run the code on every frame. So we get back the delta, which is the change between every frame, and then we can just multiply it by 0.5 to slow it down and then we can just apply rotation.y on the mesh and that is basically it. Now we can use animations in Threlte.
Translating Three.js Examples to Threlte
Let me show you how we can translate Three.js examples to Threlte. And this is very important because most of the time you're going to be looking at Three.js examples. So let's take for example the sphere geometry from their docs. And this example isn't even complete because it lacks this wireframe. But as we can see here, they're creating this geometry, material, and the sphere mesh at the end. And then they're adding it to the scene. You see if you read these docs, they have all these arguments you can pass here. And of course, if you have TypeScript in your editor, then you probably don't even need this. But as you can see, this extends all the classes such as BufferGeometry. And if you go to BufferGeometry, you can even see more arguments that we can pass to it. And that is basically how you read Three.js docs.
So let's look at an example. In this example, I have a simple camera, a sphere geometry, and a wireframe geometry that was absent from the Three.js example. So let's convert this same example to Threlte.
So the first thing I'm going to do is import Canvas from @threlte/core. So let's add the camera first. As you can see the camera comes from the three namespace. So we can just say T.PerspectiveCamera and we have to say makeDefault because Threlte provides a default camera. So you can see we can just easily convert all of these props including field of view and the position.z.
Next let's add the sphere geometry. So we can see that three.Mesh accepts a geometry and material. So we know that it's nested and that is why T.SphereGeometry and T.MeshBasicMaterial are nested inside of T.Mesh. And we can also include rotation.x. The only thing left to do is add the wireframe. And if you look at the Three.js code, the wireframe is a children of mesh. So we can also just include it as a child of mesh. And to pass the geometry and the material from the mesh to the wireframe arguments, we can use bind:ref. And as you can see, we get the same result. But there's even a simpler method and that is using the @children snippet. Snippets in Svelte are just render functions. So we can use this @children snippet and get a reference to the mesh and then we can pass the geometry and the material from the mesh to the wireframe.
And as a bonus, if you want to make this example look the same as the Three.js example, we have to pass in noToneMapping from three because there are some slight differences between Three.js and Threlte. But of course that is completely optional.
Understanding Threlte Component Props
The Threlte docs do a great job of explaining things in more detail. So for example, we'll learn that we can use this dot notation when it comes to T. So we can say T.Mesh for example, but we can also pass this is prop. And this is prop is very interesting because we can just pass a class instance from Three.js itself for example. So you can see we can import Mesh, BoxGeometry and MeshBasicMaterial from Three.js and we can just pass it to T as this is prop. And this might not look useful, but it's very useful in this example where we have like this orbit controls which we import from Three examples. So this isn't something that's available on the three namespace. In this case, we can just pass this class to the is prop.
And another interesting thing when it comes to props is the type of props that you can pass. So for example, we learned that we can pass this position prop. So this is an array of a vector three type. So we're passing X, Y, and Z to this mesh. But we also seen that we can do something like this. So we can say position.y={1} and this is called a pierce prop and this exists for two reasons. The first reason is to match the Three.js API more exactly, and the second one is that this makes it easier on Threlte because you're passing a primitive value such as a number so it's easy for Threlte to compare under the hood if the value has changed. The only downside of this approach is that it doesn't have great type completion and using a pierce prop is recommended by Threlte, but of course which type of prop you use is completely up to you.
Let's talk about Threlte extras. So what is Threlte extras? Basically @threlte/extras is a package by Threlte which provides useful utilities, abstractions and plugins for your Threlte application. So you can just install it as any other package and then you can get a bunch of cool things. So for example here they have this GLTF package where you can just import models using a URL. So they're importing this helmet just using this component in here. So if you go to scene for example here we can see this is this GLTF component. It is just passing this URL and you have a bunch of cool things here like working with HTML in your scene and you have Text3D geometry. So this looks really cool. But let's actually talk about interaction to make your life easier when you're working with 3D in Threlte. So for example here we have this gizmo. So as you can see we can navigate more easily including this orbit controls. So this makes your life a lot easier. As you can see, I can move freely in the scene like I'm using something like Blender for example, and I can just have a nicer type. So, let's look at an example.
Here we have a simple scene with a camera and a sphere. So, first let's add orbit controls to make navigating around the scene easier. So, we can import OrbitControls from @threlte/extras and then we can pass autoRotate and enableDamping props. So, this is going to rotate the camera around the scene for us. And since we have damping, it's going to make this navigation smoother for us. All right, that's already cool, but we can make it even more cooler by importing TransformControls. So now we can wrap TransformControls around our mesh. As you can notice, we need to move the position.y to TransformControls. And now we can manipulate this however we want in our scene. How beautiful is this, friends?
Creating a GUI with Svelte Tweakpane UI
Having to tweak values by hand is annoying and doesn't exactly let you be creative. So I'm going to show you how you can use this package svelte-tweakpane-ui to create a GUI where you can change those values. And in this simple example I have some lights and a sphere. The first thing we're going to do is create values that we're going to control. So we're going to create a light, ambient and sphere signal. And now we can use these values for the position, intensity and the color.
Next, we're going to import Color, Folder, Pane, and Slider from svelte-tweakpane-ui. And the last thing we have to do is construct the GUI. So, we can create a Pane with a title and then we can organize things into folders. And then we can bind our values. And now the changes in the GUI are going to be reflected in our scene. So, we can go here and play around. And this is a lot easier than having to input these values by hand. As you can see, we can do something really nice. So, we can maybe make this bluish or something. Actually, you know what? Let me actually make this super red. And let's actually mimic like atmosphere or something. So, we can make it a bit more bluish. And now it kind of looks like Mars. And this is so much nicer than having to tweak values by hand. And gives you more artistic freedom. And of course, you can learn more what you can do with Svelte Tweakpane UI by reading their docs.
Loading and Animating 3D Models
Let me show you how you can load, light, and play animations from 3D models. And if you're looking for free 3D models, there's a bunch of cool resources. One of them is Sketchfab, which is a really cool site. Just make sure that you check downloadable and then you're going to see an icon if the model is available for free. Another cool resource is Mixamo if you're looking for animated 3D characters and these are really high quality.
To show you how to load assets in Threlte, I'm going to use this 3JS example. If you want to follow along, you can find the link in the description. The most common format you're going to find for 3D models on the web is probably going to be glTF or GLB. And you can use glTF files, which is basically just a JSON. You can use the binary GLB format. But of course, there are other formats and Three.js has a bunch of loaders that you can use to import them. And not only 3D models, but audio and etc. And of course, Threlte makes this super easy.
Let's start with an empty canvas in app.svelte and then in scene.svelte we can import T from @threlte/core and then to load the model we can use the useGltf hook from @threlte/extras and in a normal case scenario you would just be able to load the model if you use the await block since it returns a promise and then you can say then and we can destructure the scene and pass it to the is prop if you remember that from before. So this is just a class instance that you can pass. But in this case, depending on the model that you're using, you might get this error: 'No Draco loader instance provided.' And this is probably because the model that we're using uses this Draco compression. And this is actually really simple to fix. We can just use useDraco from Threlte. So we can import useDraco. And you can see everything works kind of. We don't actually see anything because there is no light source.
All right. So let's add some lights. But I'm not going to use a normal light because all of the materials and shadows are already baked. So it would just look ugly. Instead of that, we have a couple of options. We can use the Environment component from Threlte and then pass a HDR to light our scene. But I'm actually going to opt for a skybox from Threlte. So in app.svelte, I'm going to import the Sky component. And as you can see, we can see our model, but we're too close to it. So going back to scene.svelte, we can change the position and the scale of the model. And now we can see it. And now let's add a camera with orbit controls. So now we can actually rotate around our model. And that is how simple that is. So you can see we can orbit normally.
So next let's see how we can add animations. And for that I'm going to actually show you the output of this glTF model. So we can see we get this object. Here we can see we have a bunch of interesting things on it including the scene and etc. But the important thing is this animations. So we can actually see all of the animations here. And here is this animation that is named Take 001. Knowing that we know what animation to play. So we can use another hook from Threlte called useGltfAnimations. And now we can destructure actions from useGltfAnimations. And we can just pass it the glTF model. Then we can use an effect in Svelte to subscribe to actions, access the animation, and then when it's ready, we can press play. And that's basically it. But of course, you don't have to use an effect. You can create functions that have play, reset, stop, whatever you want. You can export them and then you can bind the component and use them elsewhere. And that's it. Now we have a beautiful scene in 3D using Threlte. How beautiful is this, friends?
Threlte CLI and Advanced Topics
Threlte recognizes that the GT workflow on the web is not ideal. As you can see, we run into some weird issues. But thankfully, Threlte provides this useful CLI utility that you can use and create everything that you need. So, for example, you can just say npx @threlte/gltf@latest. You can pass in your model and the --transform flag and it's going to generate this component for you with everything that you need. And that is how easy that is. And you can learn more in the Threlte docs.
All right friends, and those are the basics. But to be honest with you, we barely scratched the surface. So for example, if you want 3D physics, you can use something like Rapier. Then you also have things like Theatre.js. So this is a JavaScript animation library with a professional motion design tool set. So Threlte has bindings for that also. So you can check it out if you want. You can see how crazy this is. So you have this entire GUI. It makes it really easy to work with animations in 3D.
But you also have some other things. If you're interested maybe in augmented and virtual reality, you can check out XR. And then you also have layouts here. So you can use Flexbox if you need in 3D. And you have other things such as Studio. So you can turn Threlte into Blender. You get this GUI, right? So you can see it here. Let's skip the tour. You can see here you have this nice GUI that you can interact with. Let's just click on the camera. And now we can drag around. We have the scene graph whatever. But yeah, that's basically it.
You can support me by becoming a patron. And if you like what you're seeing, don't forget to like and subscribe. And I'll catch you in the next one. Peace.