---
date: 2026-6-23
---

# Post Processing

Think of post processing like putting a filter on a photo **after** you've already taken it. In Three.js, you first render your 3D scene (your models, lights, camera), and **then** you apply effects like bloom, blur, or color grading to the final image — just like adding a VSCO or Instagram filter after snapping the picture.

![post-processing graph](https://threejs.org/manual/resources/images/threejs-postprocessing.svg)

Imagine an assembly line:

1. **Scene Renderer** takes a picture of your 3D world → sends it to
2. **EffectComposer** (the foreman) → who passes it through
3. **Passes** (each workstation adds an effect) → until
4. **The final image** shows up on screen.

## basic setup

Before you can use any effects, you need to swap out Three.js's default render loop for an `EffectComposer`:

```javascript
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';

// 1. create the composer and add a RenderPass (always the first pass)
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));

// 2. add effects
const bloomPass = new UnrealBloomPass(
    new THREE.Vector2(window.innerWidth, window.innerHeight), // resolution
    0.5, // strength (0 = off, 1 = strong glow)
    0.2, // radius (how far the glow spreads)
    0.1, // threshold (only bright things glow)
);
composer.addPass(bloomPass);

// 3. replace renderer.render(scene, camera) with composer.render()
function animate() {
    requestAnimationFrame(animate);
    composer.render(); // -- this runs all passes in order
}
animate();
```

> what's a RenderPass? It's a pass that just renders the scene normally — no effect added yet. It's always the first pass so the composer has something to work with.

### bloom (grow)

Makes bright parts of your scene glow, like a lightsaber or a neon sign:

```javascript
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';

const bloomPass = new UnrealBloomPass(resolution, 0.3, 0.5, 0.1);
composer.addPass(bloomPass);
```

### vignette (dark corners)

Draws attention to the center by darkening the edges:

```javascript
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { VignetteShader } from 'three/examples/jsm/shaders/VignetteShader.js';

const vignettePass = new ShaderPass(VignetteShader);
composer.addPass(vignettePass);
```

### film grain

Adds a vintage, gritty look by overlaying noise:

```javascript
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js';

const filmPass = new FilmPass(
    0.35, // noise intensity
    0.5, // scanline intensity
    648, // scanline count
    false,
); // colorize?
composer.addPass(filmPass);
```

### hue/saturation/brightness

Adjust colors like in a photo editor:

```javascript
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { HueSaturationShader } from 'three/examples/jsm/shaders/HueSaturationShader.js';

const colorPass = new ShaderPass(HueSaturationShader);
colorPass.uniforms['hue'].value = 0.1; // shift hue
colorPass.uniforms['saturation'].value = 0.5; // increase saturation
composer.addPass(colorPass);
```

### dot screen (comic book effect)

Makes the scene look like it's printed in a comic book with dots:

```javascript
import { DotScreenPass } from 'three/examples/jsm/postprocessing/DotScreenPass.js';

const dotPass = new DotScreenPass();
dotPass.uniforms['angle'].value = 0.5; // rotation of the dot pattern
dotPass.uniforms['scale'].value = 4; // size of the dots
composer.addPass(dotPass);
```

### chain multiple effects

Order matters — each pass feeds into the next:

```javascript
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(bloomPass); // 1. glow
composer.addPass(vignettePass); // 2. darken corners on top of glow
composer.addPass(filmPass); // 3. add grain on top of that
```

Not every pass needs to run every frame, you can toggle them: `bloomPass.enabled = false; vignettePass.enabled = true;`
