Skip to content

Harnessing the Power of Threlte - Building Reactive Three.js Scenes in Svelte

Introduction

Web development has evolved to include immersive 3D experiences through libraries like Three.js. This powerful JavaScript library enables the creation of captivating 3D scenes within browsers.

Three.js: The 3D Powerhouse

Three.js democratizes 3D rendering, allowing developers of all skill levels to craft interactive 3D worlds.

Svelte Ecosystem: Svelte Cubed and Svelthree

The Svelte ecosystem presents solutions like Svelte Cubed and Svelthree, which bridges Svelte with Three.js, offering streamlined reactivity for 3D web experiences.

Introducing Threlte v6: Uniting SvelteKit 1.0, Svelte 4, and TypeScript

Threlte v6 is a rendering and component library for Svelte that seamlessly integrates Three.js. By harnessing TypeScript's types, it provides a robust and delightful coding experience.

In this tutorial, we'll showcase Threlte's capabilities by building an engaging website header: an auto-rotating sphere that changes color on mouse down. Using Threlte v6, SvelteKit 1.0, and Three.js, we're set to create a visually stunning experience. Let's dive in!

Screenshot 2023-08-14 091949

Setting up Threlte

Before building our scene, we need to set up Threlte. We can scaffold a new project using the CLI or manually install Threlte in an existing project.

Option 1: Scaffold a New Threlte Project

Create a new SvelteKit project and install Threlte with:

npm create threlte my-project

Option 2: Manual Installation

For an existing project, select the necessary Threlte packages and install:

npm install three @threlte/core @threlte/extras @threlte/rapier @dimforge/rapier3d-compat @threlte/theatre @theatre/core @theatre/studio @types/three

Configuration adjustments for SvelteKit can be made in the "vite.config.js" file:

const config = {
  // ...
  ssr: {
    noExternal: ['three']
  }
};

With Threlte configured, we're ready to build our interactive sphere. In the next chapter, we'll lay the groundwork for our exciting 3D web experience!

Exploring the Boilerplate of Threlte

Upon scaffolding a new project using npm create threlte, a few essential boilerplate files are generated. In this chapter, we'll examine the code snippets from three of these files: lib/components/scene.svelte, routes/+page.svelte, and lib/components/app.svelte.

  1. lib/components/scene.svelte: This file lays the foundation for our 3D scene. Here's a brief breakdown of its main elements:

    • Perspective Camera: Sets up the camera view with a specific field of view and position, and integrates OrbitControls for auto-rotation and zoom management.
    • Directional and Ambient Lights: Defines the lighting conditions to illuminate the scene.
    • Grid: A grid structure to represent the ground.
    • ContactShadows: Adds shadow effects to enhance realism.
    • Float: Wraps around 3D mesh objects and defines floating properties, including intensity and range. Various geometrical shapes like BoxGeometry, TorusKnotGeometry, and IcosahedronGeometry are included here.
  2. routes/+page.svelte: This file handles the ui of the index page and imports all necessary components we need to bring our vibrant design to life.

  3. lib/components/app.svelte: This file is where you would typically define the main application layout, including styling and embedding other components.

Heading to the Fun Stuff

With the boilerplate components explained, we're now ready to dive into the exciting part of building our interactive 3D web experience. In the next section, we'll begin crafting our auto-rotating sphere, and explore how Threlte's robust features will help us bring it to life.

Creating a Rotating Sphere Scene

In this chapter, we'll walk you through creating an interactive 3D sphere scene using Threlte. We'll cover setting up the scene, the sphere, the camera and lights, and finally the interactivity that includes a scaling effect and color changes.

1. Setting Up the Scene

First, we need to import the required components and utilities from Threlte.

import { T } from '@threlte/core';
import { OrbitControls } from '@threlte/extras';

2. Setting Up the Sphere

We'll create the 3D sphere using Threlte's <T.Mesh> and <T.SphereGeometry> components.

<T.Mesh>
	<T.SphereGeometry args={[1, 32, 32]} />
	<T.MeshStandardMaterial color={`rgb(${sphereColor})`} roughness={0.2} />
</T.Mesh>
  1. <T.Mesh>: This is a component from Threlte that represents a 3D object, which in this case is a sphere. It's the container that holds the geometry and material of the sphere.

  2. <T.SphereGeometry args={[1, 32, 32]} />: This is the geometry of the sphere. It defines the shape and characteristics of the sphere. The args attribute specifies the parameters for the sphere's creation:

    • The first argument (1) is the radius of the sphere.
    • The second argument (32) represents the number of width segments.
    • The third argument (32) represents the number of height segments.
  3. <T.MeshStandardMaterial color={rgb(${sphereColor})} roughness={0.2} />: This is the material applied to the sphere. It determines how the surface of the sphere interacts with light. The color attribute specifies the color of the material. In this case, the color is dynamic and defined by the sphereColor variable, which updates based on user interaction. The roughness attribute controls the surface roughness of the sphere, affecting how it reflects light.

3. Setting Up the Camera and Lights

Next, we'll position the camera and add lights to create a visually appealing scene.

<T.PerspectiveCamera makeDefault position={[-10, 20, 10]} fov={15}>
	<OrbitControls
		enableZoom={false}
		enablePan={false}
		enableDamping
		autoRotate
		autoRotateSpeed={1.5}
	/>
</T.PerspectiveCamera>
<T.DirectionalLight intensity={0.8} position.x={10} position.y={10} />
<T.AmbientLight intensity={0.02} />
  1. <T.PerspectiveCamera>: This component represents the camera in the scene. It provides the viewpoint through which the user sees the 3D objects. The position attribute defines the camera's position in 3D space. In this case, the camera is positioned at (-10, 20, 10). The fov attribute specifies the field of view, which affects how wide the camera's view is.

    • makeDefault: This attribute makes this camera the default camera for rendering the scene.
  2. <OrbitControls>: This component provides controls for easy navigation and interaction with the scene. It allows the user to pan, zoom, and orbit around the objects in the scene. The attributes within the <OrbitControls> component configure its behavior:

    • enableZoom: Disables zooming using the mouse scroll wheel.
    • enablePan: Disables panning the scene.
    • enableDamping: Enables a damping effect that smoothens the camera's movement.
    • autoRotate: Enables automatic rotation of the camera around the scene.
    • autoRotateSpeed: Defines the speed of the auto-rotation.
  3. <T.DirectionalLight>: This component represents a directional light source in the scene. It simulates light coming from a specific direction. The attributes within the <T.DirectionalLight> component configure the light's behavior:

    • intensity: Specifies the intensity of the light.
    • position.x and position.y: Define the position of the light source in the scene.
  4. <T.AmbientLight>: This component represents an ambient light source in the scene. It provides even lighting across all objects in the scene. The intensity attribute controls the strength of the ambient light.

4. Interactivity: Scaling and Color Changes

Now we'll add interactivity to the sphere, allowing it to scale and change color in response to user input.

First, we'll import the required utilities for animation and set up a spring object to manage the scale.

import { spring } from 'svelte/motion';
import { onMount } from 'svelte';
import { interactivity } from '@threlte/extras';

interactivity();

const scale = spring(0, { stiffness: 0.1 });

onMount(() => {
	scale.set(1);
});

We'll update the sphere definition to include scaling:

<T.Mesh scale={$scale} on:pointerenter={() => scale.set(1.1)} on:pointerleave={() => scale.set(1)}>
	<T.SphereGeometry args={[1, 32, 32]} />
	<T.MeshStandardMaterial color={`rgb(${sphereColor})`} roughness={0.2} />
</T.Mesh>

Lastly, we'll add code to update the color of the sphere based on the mouse's position within the window.

let mousedown = false;
let rgb: number[] = [];

function updateSphereColor(e: MouseEvent) {
	if (mousedown) {
		rgb = [
			Math.floor((e.pageX / window.innerWidth) * 255),
			Math.floor((e.pageY / window.innerHeight) * 255),
			150
		];
	}
}
window.addEventListener('mousedown', () => (mousedown = true));
window.addEventListener('mouseup', () => (mousedown = false));
window.addEventListener('mousemove', updateSphereColor);

$: sphereColor = rgb.join(',');

We have successfully created a rotating sphere scene with scaling and color-changing interactivity. By leveraging Threlte's capabilities, we have built a visually engaging 3D experience that responds to user input, providing a dynamic and immersive interface.

Adding Navigation and Scroll Prompt in app.svelte

In this chapter, we'll add a navigation bar and a scroll prompt to our scene. The navigation bar provides links for user navigation, while the scroll prompt encourages the user to interact with the content. Here's a step-by-step breakdown of the code:

1. Importing the Canvas and Scene

The Canvas component from Threlte serves as the container for our 3D scene. We import our custom Scene component to render within the canvas.

import { Canvas } from '@threlte/core';
import Scene from './Scene.svelte';

2. Embedding the 3D Scene

The Canvas component wraps the Scene component to render the 3D content. It is positioned absolutely to cover the full viewport, and the z-index property ensures that it's layered behind the navigation elements.

<div class="sphere-canvas">
	<Canvas>
		<Scene />
	</Canvas>
</div>

3. Adding the Navigation Bar

We use a <nav> element to create a horizontal navigation bar at the top of the page. It contains a home link and two navigation list items. The styling properties ensure that the navigation bar is visually appealing and positioned correctly.

<nav>
	<a href="/">Home</a>
	<ul>
		<li>Explore</li>
		<li>Learn</li>
	</ul>
</nav>

4. Adding the Scroll Prompt

We include a "Give a scroll" prompt with an <h1> element to encourage user interaction. It's positioned near the bottom of the viewport and styled for readability against the background.

<h1>Give a scroll</h1>

5. Styling the Components

Finally, the provided CSS styles control the positioning and appearance of the canvas, navigation bar, and scroll prompt. The CSS classes apply appropriate color, font, and layout properties to create a cohesive and attractive design.

.sphere-canvas {
	width: 100%;
	height: 100%;
	position: absolute;
	top: 0;
	left: 0;
	z-index: 1;
}

nav {
	display: flex;
	color: white;
	z-index: 2;
	position: relative;
	padding: 4rem 8rem;
	justify-content: space-between;
	align-items: center;
}

nav a {
	text-decoration: none;
	color: white;
	font-weight: bold;
}

nav ul {
	display: flex;
	list-style: none;
	gap: 4rem;
}

h1 {
	color: white;
	z-index: 2;
	position: absolute;
	font-size: 3rem;
	left: 50%;
	top: 75%;
	transform: translate(-50%, -75%);
}

Head to the github repo to view the full code.

Check out the result: https://threlte6-spinning-ball.vercel.app/

Conclusion

We've successfully added navigation and a scroll prompt to our Threlte project in the app.svelte file. By layering 2D HTML content with a 3D scene, we've created an interactive user interface that combines traditional web design elements with immersive 3D visuals.