DEV Community

Cover image for Svelte-Cubed: An Introduction to 3D in the Browser
Alex Warnes
Alex Warnes

Posted on

Svelte-Cubed: An Introduction to 3D in the Browser

Whatever you're making from data visualizations, or telling stories, being creative, selling products, 3D scenes open up new ways for people to engage with your content. (and it's also just really fun)

This article will walk through the basics of:

  1. Setting up a scene
  2. Adding lights and shapes
  3. Incorporating the scene into a site

Tools

We'll be using svelte, three.js, and svelte-cubed.

If you're unfamiliar with these tools and just want to make something that looks cool you can still follow along! And if you want to learn more, that's great too. Here are some helpful introductory resources you can come back to later (or banish into eternal browser-tab land):

The best way to learn is to build, so let's go.

(full code is at the bottom if you run into any issues)

Setting up a Scene: Canvas and Camera

Head on over to the Svelte REPL: https://svelte.dev/repl/

This is where we'll do all our development so you can easily share your scene and won't have to worry about setting anything up on your machine.

Start by importing our main tools in the script tag:

<script>
import * as THREE from "three";
import * as SC from "svelte-cubed";

// all of our javascript and logic would go here

</script>
Enter fullscreen mode Exit fullscreen mode

This enables us to use THREE and SC inside our App.svelte file. (And don't worry about bundle size; the Svelte compiler will optimize that for us)

Every 3D scene is projected onto a canvas, so let's create one. Under the script tags, add a svelte-cubed canvas:

<SC.Canvas>

<!-- all of our scene stuff will go here! -->

</SC.Canvas>
Enter fullscreen mode Exit fullscreen mode

Way to go! Now buy my book... just kidding. Let's continue.

We can't see anything because we have no camera. We'll go with the classic perspective camera (you can experiment with the orthographic camera at the end to see how it differs).


<!-- CAMERA -->
<SC.PerspectiveCamera near={1} far={100} fov={55} />

Enter fullscreen mode Exit fullscreen mode

wait wut? Okay, so we have to configure our camera a little bit to create our view frustum aka what our camera can see.

  • near: anything closer than this will not be shown by the camera (unit: meters)
  • far: anything farther than this will not be shown by the camera (unit: meters)
  • fov: (Field of View) anything outside of this angle will not be shown by the camera (unit: degrees)

Let's see if it's working by creating a new color and setting it as our canvas background:


<SC.Canvas background={new THREE.Color('seagreen')}>

  <!-- CAMERA -->
  <SC.PerspectiveCamera near={1} far={100} fov={55} />

<!-- all of our scene stuff will go here! -->

</SC.Canvas>

Enter fullscreen mode Exit fullscreen mode

Success!

A seagreen html canvas without any objects on it.

Adding Shapes

What regular people call shapes are called a "mesh" in 3D-land, so we'll use that word from now on. A mesh is a combination of a geometry and a material, just like your table: it has a geometry (e.g. rectangle or oval) and a material (e.g. wood or glass).

Three.js offers many geometries and materials you can experiment with, and each variant can take several unique properties. For now let's add an octahedron... and together we'll learn what an octahedron is.


<!-- MESHES -->
<SC.Mesh
  geometry={new THREE.OctahedronGeometry()}
  material={new THREE.MeshStandardMaterial({
    color: new THREE.Color('salmon')
  })}
/>

Enter fullscreen mode Exit fullscreen mode

Notice that we passed a javascript object into our new material with a color property. This is where you can handle all sorts of material enhancements, but we'll just use color for now.

And there it is! But it's not the color of salmon :( That's happening because our MeshStandardMaterial interacts with light and we don't have any lights! (You can experiment with a MeshBasicMaterial for something that does not interact with light to compare)

A seagreen html canvas without an octahedron that is entirely black because there is no light.

Adding Light

Unsurprisingly three.js offers many lighting options. Not sure how to incorporate this pun but: something, something, the unbearable lightness of three-ing. Now we're going to add two lights: a directional light and an ambient light, and you'll probably get the gist of what they do in the process.


<SC.DirectionalLight
  color={new THREE.Color('white')}
  intensity={.75}
  position={[10, 10, 10]}
/>

Enter fullscreen mode Exit fullscreen mode

A seagreen html canvas with a dimly lit salmon-color octahedron.

Notice how the un-lit sides are really dark. Let's add the ambient light:


  <SC.AmbientLight
    color={new THREE.Color('white')}
    intensity={.5}
  />

Enter fullscreen mode Exit fullscreen mode

A seagreen html canvas with a well lit salmon-color octahedron.

Looking good! But still only looks 2D because we're looking at the mesh straight on.

We can move the mesh with its rotation or position properties, or we can move the camera with its position property, but to get a full range of interactions we'll add orbit controls after our camera.


<!-- CAMERA -->
<SC.PerspectiveCamera near={1} far={100} fov={55} />

<SC.OrbitControls
  enabled
  enablePan
  enableZoom
  enableRotate
  enableDamping
/>

Enter fullscreen mode Exit fullscreen mode

Now you can interact with your scene by clicking and dragging, scrolling to zoom, and right-click and drag to pan.

A camera rotating around a well lit salmon-color octahedron in front of a seagreen html canvas.

COOL. But none of your friends know what an octahedron is, so you want to include this scene within the context of a regular website with some words. Svelte (and svelte-cubed) make this incredibly easy.

Adding the Scene to Your Site

Let's move ALL our code into a new component.

  1. Copy all the code (script included) in App.svelte
  2. Click the + icon to create a new file and name it Octo.svelte
  3. Paste all your code in Octo.svelte
  4. Delete all the code in App.svelte

Now we can't see anything. App.svelte is the entry point for your application, and it's empty so the page is empty.

Let's add some markup in App.svelte:

<h1>What is an Octahedron?</h1>

<div class="scene-container"></div>

<p>An octahedron is a three-dimensional shape having eight plane faces, especially a regular solid figure with eight equal triangular faces.</p>
Enter fullscreen mode Exit fullscreen mode

We'll use the "scene-container" div to contain our scene, and whatever dimensions we set on this container will control our scene dimensions too! Pretty neat.

Import your Octo.svelte component and nest it inside the "scene-container" div:

<script>
  import Octo from "./Octo.svelte";
</script>
Enter fullscreen mode Exit fullscreen mode

And drop it in the markup like an element:

<div class="scene-container">
  <Octo />
</div>
Enter fullscreen mode Exit fullscreen mode

Uh oh, it's taking over the whole page because we haven't styled the "scene-container". In App.svelte add the style tags under your markup:

<style>
  .scene-container {
    /* position relative let's the canvas position itself relative to this container */
    position: relative;
    width: 75%;
    max-width: 400px
    height: 400px;
    margin: 0 auto;
  }
</style
Enter fullscreen mode Exit fullscreen mode

And there you go! An informative geometry themed page with a responsive and interactive 3D scene. But you're just getting started.

Add more shapes, change the camera, add wireframe: true to the mesh material properties. What if you change the mesh position to animate movement? What if you change the directional light position to change the angle of illumination? What about importing models and animating scenes?

If you enjoyed this and want to do more experiments, I built a little open source tool to help with some basics that will generate the svelte code for you to build on: https://sc3-lab.netlify.app/

The plan is to write a couple more of these for movement, transitions, and importing models. Until then, share what you built and be proud of it!

A robot giving you a thumbs up.

For Reference

REPL

https://svelte.dev/repl/71b063fc410543598e8a727999cf7bbe

App.svelte

<script>
  import Octo from "./Octo.svelte";
</script>
<h1>What is an Octahedron?</h1>

<div class="scene-container">
  <Octo></Octo>
</div>

<p>An octahedron is a three-dimensional shape having eight plane faces, especially a regular solid figure with eight equal triangular faces.</p>

<style>
.scene-container {
/* position relative let's the canvas position itself relative to this container */
  position: relative; 
  width: 75%;
  max-width: 400px;
  height: 400px;
  margin: 0 auto;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Octo.svelte

<script>
import * as THREE from "three";
import * as SC from "svelte-cubed";
</script>

<SC.Canvas background={new THREE.Color('seagreen')}>

  <!-- LIGHTS -->
  <SC.AmbientLight
    color={new THREE.Color('white')}
    intensity={.5}
  />  
  <SC.DirectionalLight
    color={new THREE.Color('white')}
    intensity={.75}
    position={[10, 10, 10]}
  />

  <!-- MESHES -->
   <SC.Mesh
    geometry={new THREE.OctahedronGeometry()}
    material={new THREE.MeshStandardMaterial({
      color: new THREE.Color('salmon')
    })}
  />

  <!-- CAMERA -->
  <SC.PerspectiveCamera near={1} far={100} fov={55} />      
  <SC.OrbitControls
    enabled
    enablePan
    enableZoom
    enableRotate
    enableDamping
  />

</SC.Canvas>

Enter fullscreen mode Exit fullscreen mode

Discussion (0)