---
name: postfx-composer
description: EffectComposer setup and architecture for Three.js post-processing pipelines. Use when setting up multi-pass rendering, combining effects, creating custom passes, managing render targets, or building reusable effect stacks. Foundation skill for all post-processing work.
---
# Post-Processing Composer
EffectComposer architecture, render pipelines, and custom pass creation.
## Quick Start
```bash
npm install @react-three/postprocessing postprocessing three
```
```tsx
import { Canvas } from '@react-three/fiber';
import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing';
function App() {
return (
);
}
```
## Core Concepts
### Render Pipeline Flow
```
Scene Render → EffectComposer → Effect 1 → Effect 2 → ... → Screen
↓
[Render Target A] → [Render Target B] → [Screen]
```
### EffectComposer Configuration
```tsx
import { EffectComposer } from '@react-three/postprocessing';
import { HalfFloatType } from 'three';
function PostProcessing() {
return (
{/* Effects processed in order */}
);
}
```
## Effect Ordering
### Recommended Order
```tsx
{/* 1. Scene modification (needs scene data) */}
{/* 2. Lighting/color effects */}
{/* 3. Color grading */}
{/* 4. Screen-space effects */}
{/* 5. Noise/grain (always last) */}
```
### Why Order Matters
```tsx
// WRONG: Bloom after color grading reduces glow
// CORRECT: Bloom before color grading
```
## Conditional Effects
### Toggle Effects at Runtime
```tsx
function DynamicEffects({ enableBloom, enableDOF }) {
return (
{enableBloom && }
{enableDOF && }
);
}
```
### Effect Presets
```tsx
const PRESETS = {
cinematic: {
bloom: { intensity: 0.8, threshold: 0.3 },
vignette: { darkness: 0.5, offset: 0.3 },
grain: { opacity: 0.03 }
},
scifi: {
bloom: { intensity: 2.0, threshold: 0.1 },
chromatic: { offset: [0.003, 0.003] },
vignette: { darkness: 0.6, offset: 0.2 }
},
minimal: { vignette: { darkness: 0.3, offset: 0.4 } }
};
function PresetEffects({ preset = 'cinematic' }) {
const config = PRESETS[preset];
return (
{config.bloom && }
{config.chromatic && }
{config.vignette && }
{config.grain && }
);
}
```
## Custom Effects
### Basic Custom Effect
```tsx
import { Effect } from 'postprocessing';
import { Uniform } from 'three';
import { forwardRef, useMemo } from 'react';
class ColorShiftEffect extends Effect {
constructor({ shift = 0.0 }) {
super('ColorShiftEffect', /* glsl */`
uniform float shift;
void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {
vec3 color = inputColor.rgb;
float hueShift = shift * uv.x;
color.r = color.r * cos(hueShift) - color.g * sin(hueShift);
color.g = color.r * sin(hueShift) + color.g * cos(hueShift);
outputColor = vec4(color, inputColor.a);
}
`, {
uniforms: new Map([['shift', new Uniform(shift)]])
});
}
set shift(value) {
this.uniforms.get('shift').value = value;
}
}
const ColorShift = forwardRef(({ shift = 0 }, ref) => {
const effect = useMemo(() => new ColorShiftEffect({ shift }), []);
useEffect(() => { effect.shift = shift; }, [shift, effect]);
return ;
});
```
### Custom Effect with Time
```tsx
class PulseEffect extends Effect {
constructor() {
super('PulseEffect', /* glsl */`
uniform float uTime;
uniform float uIntensity;
void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {
float pulse = sin(uTime * 2.0) * 0.5 + 0.5;
float vignette = 1.0 - length(uv - 0.5) * pulse * uIntensity;
outputColor = vec4(inputColor.rgb * vignette, inputColor.a);
}
`, {
uniforms: new Map([
['uTime', new Uniform(0)],
['uIntensity', new Uniform(0.5)]
])
});
}
update(renderer, inputBuffer, deltaTime) {
this.uniforms.get('uTime').value += deltaTime;
}
}
```
## Render Targets
### Manual Render Target Management
```tsx
import { useFBO } from '@react-three/drei';
function CustomRenderPipeline() {
const { gl, scene, camera } = useThree();
const targetA = useFBO({ width: 1024, height: 1024 });
useFrame(() => {
gl.setRenderTarget(targetA);
gl.render(scene, camera);
gl.setRenderTarget(null);
gl.render(scene, camera);
});
return null;
}
```
## Performance Patterns
### Resolution Scaling
```tsx
function ScaledEffects() {
const { size } = useThree();
const scale = 0.5;
return (
);
}
```
### Adaptive Quality
```tsx
import { useDetectGPU } from '@react-three/drei';
function AdaptiveQuality() {
const { tier } = useDetectGPU();
const quality = useMemo(() => {
if (tier >= 3) return { multisampling: 8, bloomLevels: 8 };
if (tier >= 2) return { multisampling: 4, bloomLevels: 5 };
return { multisampling: 0, bloomLevels: 3 };
}, [tier]);
return (
);
}
```
## Temporal Collapse Setup
```tsx
import { EffectComposer, Bloom, ChromaticAberration, Vignette, Noise, ToneMapping } from '@react-three/postprocessing';
import { ToneMappingMode, BlendFunction } from 'postprocessing';
import { HalfFloatType } from 'three';
function TemporalCollapseComposer({ children }) {
return (
{children}
);
}
```
## Reference
- See `postfx-bloom` for bloom-specific techniques
- See `postfx-effects` for individual effect configurations
- See `shaders-glsl` for writing custom effect shaders