Shortcuts ⌨️

  • SPACE to play / pause
  • ARROW RIGHT or L to go forward
  • ARROW LEFT or J to go backward
  • ARROW UP to increase volume
  • ARROW DOWN to decrease volume
  • F to toggle fullscreen
  • M to toggle mute
  • 0 to 9 to go to the corresponding part of the video
  • SHIFT + , to decrease playback speed
  • SHIFT + . or ; to increase playback speed

⚠️ Update

If you are using your own model and you notice that the light isn’t where it should be, you probably changed the rotation or the scale in Blender.

To fit it, add them like for the position:

    geometry={ nodes.poleLightA.geometry }
    position={ nodes.poleLightA.position }
    rotation={ nodes.poleLightA.rotation }
    scale={ nodes.poleLightA.scale }

Unlock content 🔓

To get access to 93 hours of video, a members-only Discord server, subtitles, lesson resources, future updates and much more join us for only $95!

Want to learn more? 👋


That's the end of the free part 😔

To get access to 93 hours of video, a members-only Discord server and future updates, join us for only $95!

Next lesson

Portal Scene

Difficulty Medium

Introduction 00:00

Let’s continue our process of repeating the original lessons with R3F.

This time, we are going to recreate the portal scene.

We are not going to re-do the whole modelling, baking, and exporting, since those steps aren’t related to R3F.

You can use your own portal if you followed the previous lessons or you can use the one provided with the lesson’s starter.

Setup 01:22

The starter setup is the same as in the previous lesson:

We have one cube at the center of the scene and OrbitControls so that we can rotate it around.

We didn’t use Perf because we shouldn’t have any performance concerns, but feel free to add it if you want.

The shaders of the portal (vertex and fragment) are already located in /src/shaders/portal/ . Why the fireflies shaders are missing, will become clear later in the lesson.

Dark background 02:32

This time, we are going to keep the colors of the original lesson. This means that we need a darker background. There are multiple ways of doing that as we saw in a previous lesson.

Let’s use the <color> that we attach to the scene technique.

In Experience JSX, create a <color>:

export default function Experience()
    return <>

        <color />

        {/* ... */}


Send it an array containing the color using the args attribute:

<color args={ [ '#030202' ] } />

And attach it to the scene background with the attach attribute:

<color args={ [ '#030202' ] } attach="background" />

The model 04:30

It’s time to add our model.


The model file is named portal.glb and is located in the /public/model/ folder. Feel free to replace it with your own model.

We are going to use useGLTF.

Import useGLTF from @react-three/drei:

import { useGLTF, OrbitControls } from '@react-three/drei'

And load the model (don’t forget that we don’t need to write public/:

export default function Experience()
    const model = useGLTF('./model/portal.glb')

    // ...

If you remember the lesson in which we created and imported the model, it is composed of multiple parts:

  • The baked model to which we need to apply a MeshBasicMaterial with the baked texture.
  • Two pole lights Meshes to which we need to apply a MeshBasicMaterial
  • The portal to which we need to apply a ShaderMaterial

Because of that, we are not going to add the whole model at once to the scene.

Instead, we are going to add each element separately in order to have more control over them.

And those elements are already available in the nodes property:

const model = useGLTF('./model/portal.glb')

Those names correspond to the name we chose in Blender. If you are using your own model, make sure to adapt to your configuration.

And again, we can destructure it:

const { nodes } = useGLTF('./model/portal.glb')

Baked model

The baked model node is named baked and we can now access it with nodes.baked.

This time, we are not going to use <primitive> because we want to apply our own material to the Mesh. But we do need the geometry.

In the JSX, create a <mesh> and set its geometry to nodes.baked.geometry:

export default function Experience()
    // ...

    return <>

        {/* ... */}

        <mesh geometry={ nodes.baked.geometry } />


We now have our model and we can get rid of the cube:

By default, a MeshBasicMaterial is applied to the <mesh> which is why we can see something.

We are going to add our own material, but first, we need to load the texture.

To load the texture, we can use the useTexture helper from drei.

Want to learn more?

That's the end of the free part 😔

To get access to 93 hours of video, a members-only Discord server and future updates, join us for only $95!

How to use it 🤔

  • Download the Starter pack or Final project
  • Unzip it
  • Open your terminal and go to the unzip folder
  • Run npm install to install dependencies
    (if your terminal warns you about vulnerabilities, ignore it)
  • Run npm run dev to launch the local server
    (project should open on your default browser automatically)
  • Start coding
  • The JS is located in src/script.js
  • The HTML is located in src/index.html
  • The CSS is located in src/style.css

If you get stuck and need help, join the members-only Discord server: