Threlte first scene

configure threlte efficiently

Efficiently configure Threlte scene


Threlte is a 3D framework for the web. Built on top of Svelte and Three.js.

Threlte first scene

Installation & Configuration

  1. Install required packages
    npm install three @threlte/core \
             @threlte/extras \
             @threlte/rapier @dimforge/rapier3d-compat \
             @threlte/theatre @theatre/core @theatre/studio \
    This will install all necessary packages for threlte
  2. Configure vite.config.js
    import { sveltekit } from '@sveltejs/kit/vite';
    import { defineConfig } from 'vitest/config';
    export default defineConfig({
       plugins: [sveltekit()],
       test: {
          include: ['src/**/*.{test,spec}.{js,ts}']
       ssr: {
          noExternal: ['three']
    File structure
  3. Every threlte consists of a couple of components3a. Route page aka the +page.svelte [Contains Canvas from threlte core]3b. 3d Model File, named like 3dModelName.svelte [normally under lib folder, this file is dedicated to the model configuration, material, texture, model loader, etc. ]3c. Scene File named like Scene3.svelte [HDR, Camera, Grid & import the model file here]

Create svelte threlte component from GLB file

  1. Export the glb model from blender (eg. PaperCup_Tutorial.glb)
  2. Copy the glb model to the static folder
  3. Open terminal from the sveltekit project folder, using the terminal change your directory to the model folder.
  4. Run
    npx @threlte/gltf@next ./PaperCup_Tutorial.glb
  5. This will create a svelte component named PaperCup_Tutorial.svelteCreating the scene fileHere is an example of a scene file, Usually I create this file under Scenes folder inside the lib folder Check the provided code, I have tried to include the required snippet, you must update the code based on your requirements. You must update the Environment path and files name (In my case, I have a HDR file named stadium_01_2k.hdr resides under static/hdr folder, the hdr file downloaded from hdrheaven)
       import { T } from '@threlte/core';
       import {
          } from '@threlte/extras';
       import { BoxGeometry, DirectionalLightHelper, MeshStandardMaterial } from 'three';
       import { spring, tweened } from 'svelte/motion';
    <!-- This requires a .hdr file inside static folder -->
    <Environment path="/hdr/" files="stadium_01_2k.hdr" isBackground={false} />
    <T.PerspectiveCamera makeDefault position={[3, 3, 10]} lookAt.y={0.0} fov={30}>
       <OrbitControls enableDamping />
    <T.GridHelper args={[10, 10]} />
       <T.Mesh position={[0, 0.5, 0]} scale={7}>
          <T.SphereGeometry />
          <T.MeshStandardMaterial roughness={0} color="gray" side={0} />
    <HTML position.y={2} scale={0.5} transform occlude={false} sprite={false} center>
       <button class="btn btn-primary"> Hello Threlte </button>
    <ContactShadows scale={20} blur={2} far={2.5} opacity={1} position={[0, -1, 0]} />

Creating the route file

  1. Open +page.svelte file, Import Canvas and sceneOne (this is the file where we will build the scene)file from library
    import { Canvas } from '@threlte/core';
    import SceneOne from '../lib/SceneOne.svelte';
  2. Inside +page.svelte file, Create a section where we want the canvas to live, the canvas is our placeholder to render all three js objects and interactivities
    <section class="p-5">
       <div class="canvas-wrapper ring h-[500px]">
             <SceneOne />
  3. Let’s update the sceneOne.svelte file, in my case, resides under the lib folder which is under the src folder.Import required packages
    import { PerspectiveCamera, DirectionalLight, AmbientLight, Mesh, useFrame } from '@threlte/core';
    import { MeshStandardMaterial, IcosahedronGeometry } from 'three';
    import { tweened } from 'svelte/motion';
    // tweened take a new number and go from defined number (which is 3 in this case) to that new number
    // within defined duration (which is 2000 in our case)
    const t = tweened(0, { duration: 2000 });
    const t2 = tweened(1, { duration: 50 });
    // setTimeout(() => {
    // 	t.set(-3);
    // });
    let rotation = 0;
    useFrame(() => {
        rotation += 0.01;
  4. Now create camera, light, and mesh
       <PerspectiveCamera position={{ y: 0, z: 20 }} lookAt={{ x: 0, y: 0, z: 0 }} />
       <DirectionalLight />
       <AmbientLight />
       <!-- when we want to interact with any 3d object we must set the properties to 'interactive' -->
          geometry={new IcosahedronGeometry()}
          material={new MeshStandardMaterial({ color: 'seagreen' })}
          position={{ x: $t, y: 0, z: 15 }}
          rotation={{ x: rotation, y: 0, z: rotation }}
          on:pointerleave={() => {
             $t2 = 1;
             // console.log('reverse pointer enter');
          on:pointerenter={() => {
             $t2 = 1.3;
             // console.log('pointer enter');
          on:click={() => {
             $t = $t > 0 ? -4 : 4;
    That’s pretty much it