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

Use version 2.15 of @react-three/postprocessing:

npm install @react-three/postprocessing@2.15

⚠️ Update

You might notice that all materials are changing when adding <EffectComposer>.

It’s a bug in the latest versions of @react-three/postprocessing due to EffectComposer rendering the scene with MeshNormalMaterial for later use in the effects.

Since this normal pass isn’t necessary for the rest of the lesson, you can deactivate it by adding the disableNormalPass:

<EffectComposer disableNormalPass>

⚠️ Update

In the latest versions of Three.js, the tone mapping won’t work anymore when using Post Processing.

To fix that, you must import ToneMapping from @react-three/postprocessing:

import { ToneMapping, Vignette, EffectComposer } from '@react-three/postprocessing'

Add it at the end of the <EffectComposer> and keep it throughout the whole lesson:

<EffectComposer disableNormalPass>
    <Vignette />
    <ToneMapping />

You might notice color variations compared to the video.

⚠️ Update

Use version 6.35 of postprocessing:

npm install postprocessing@6.35

⚠️ Update

Since we are using <ToneMapping>, there is a difference and you can see everything glowing.

You can fix that by adding a luminanceThreshold to 1.1:

<Bloom luminanceThreshold={ 1.1 } />

More about this attribute later.

You can also ignore the toneMapped={ false } that you’ll see in the video.

⚠️ Update

You’ll notice references to an effect named SSR and leva controls associated with it.

As this effect is unstable, it has been removed from the lesson.

Unlock content 🔓

To get access to 91 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 91 hours of video, a members-only Discord server and future updates, join us for only $95!

Next lesson


Difficulty Very hard

Introduction 00:00

Post-processing also benefits from the React and R3F system, as it has gotten easier to implement but is also optimized in some ways.

In this lesson, we are going to learn how it differs from the post-processing techniques we’ve covered before, implement a bunch of effects, and even create our own effects.

Before starting 01:20

The issue with post-processing 02:41

In the previous lessons, we’ve used post-processing by adding passes.

Those passes had their own code and were completing one or multiple renders in order to achieve the desired result.

Then the next pass would do the same, and again, and again.

Those passes all occurred independently and some of them were doing the same renders (depth renders, normal renders, etc.), which resulted in performance issues and limited the number of passes we could add before getting a frame rate drop.

The solution 04:31

And this is exactly what Post Processing is trying to resolve.

The various passes will be merged into the least number of passes possible. In fact, we don’t talk about passes anymore, but we talk about “effects”.

Those effects will be merged together into one or multiple passes (if needed) automatically while keeping the order in which we added them.

We can even choose the blending of each effect (more about that later).

And, as you might’ve guessed, we can use Post Processing in R3F with @react-three/postprocessing.

Setup 06:51

In the starter, we have the classic orange sphere, purple cube, and green floor.

We also have a directional light source and an ambient light source.

The @react-three/drei dependency is already installed within the project and we are using the OrbitControls helper to be able to move the camera around.

We also have <Perf /> from r3f-perf in order to keep an eye on performance.

Implement 07:50

We need two dependencies, @react-three/postprocessing, and postprocessing.

But for now, the only one we need to install is @react-three/postprocessing since this dependency will also install postprocessing.

In the terminal, use npm install @react-three/postprocessing@2.15 (we force the versions to prevent surprises, you can ignore potential vulnerability warnings).

In Experience.jsx, import EffectComposer from @react-three/postprocessing:

import { EffectComposer } from '@react-three/postprocessing'

Although it’s the same name as the EffectComposer we used in native Three.js, it’s not the same class.

Now, add it to the JSX:

export default function Experience()
    return <>


        {/* ... */}


All the materials changed to MeshNormalMaterial.

It’s a bug in the latest versions of @react-three/postprocessing due to EffectComposer rendering a “normal pass” to be used later in the effects.

Since this normal pass isn’t necessary for the rest of the lesson, we can deactivate it by adding the disableNormalPass attribute on <EffectComposer>:

<EffectComposer disableNormalPass>

Our scene is back to normal but with EffectComposer running.

Note that we don’t even need to add the first render, since Post Processing will take care of that.

Be careful, in the following parts, as you might have to reload the page after tweaking or adding an effect.


We can assign various attributes to the <EffectComposer>, but the most interesting one is multisample.

As discussed in the previous lessons, multi-sampling is used to prevent the aliasing effect (the little stairs on the edges of geometries).

By default, its value is at 8 and we can lower it down to 0 in order to disable it completely.

<EffectComposer disableNormalPass multisampling={ 0 }>

(Note that you will probably not see a big difference in the screenshot above because of the image compression)

Performance should be better when disabling multi-sampling, but we don’t really care about that in this lesson, so let’s remove it and keep the default value:

<EffectComposer disableNormalPass>

Finding effects and how to implement them 13:08

In the following part of the lesson, we are going to test a bunch of effects for the sake of learning.

We are going to set very specific values without going too much into detail because it would take ages and be boring.

But you should roam the documentation in order to discover the various effects, test them, and see how they work.

Unfortunately, the documentation (though useful) is a bit messy and spread across react-postprocessing and Post Processing, which means you’ll have to dig a little in order to find what you are looking for.

Here are the links that you might need.

Post Processing:


Vignette effect 18:01

Let’s start with a very common effect, the Vignette effect.

Vignette will make the corners of the render a little darker.

Want to learn more?

That's the end of the free part 😔

To get access to 91 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: