Skip to content

Pass Effects

Pass Effects are full-screen post-processing effects applied after sprite rendering. They transform the entire scene output — CRT curvature, scanlines, color quantization, VHS distortion, and more. Pass effects require a Flatland instance.

Creating a Pass Effect

Use createPassEffect with a name, a schema defining parameters, and a pass builder function:

import { createPassEffect } from 'three-flatland'
import { posterize } from '@three-flatland/nodes'
const PosterizePass = createPassEffect({
name: 'posterize',
schema: { bands: 6 },
pass: ({ uniforms }) => (input, uv) => {
return posterize(input, uniforms.bands)
},
})

The pass callback receives a context with TSL uniform nodes for each schema field and returns a PassEffectFn:

type PassEffectFn = (input: Node<'vec4'>, uv: Node<'vec2'>) => Node<'vec4'>
  • input — the scene color (or previous pass output)
  • uv — screen-space UV coordinates

Schema fields become typed properties on instances with zero-cost uniform updates — changing a value updates the GPU uniform directly without rebuilding the shader graph.

Using Pass Effects

import { Flatland, createPassEffect } from 'three-flatland'
import { posterize, crtVignette } from '@three-flatland/nodes'
const PosterizePass = createPassEffect({
name: 'posterize',
schema: { bands: 6 },
pass: ({ uniforms }) => (input, uv) => posterize(input, uniforms.bands),
})
const VignettePass = createPassEffect({
name: 'vignette',
schema: { intensity: 0.4, curvature: 2 },
pass: ({ uniforms }) => (input, uv) =>
crtVignette(input, uv, uniforms.intensity, uniforms.curvature),
})
const flatland = new Flatland({ viewSize: 400, clearColor: 0x1a1a2e })
// Add passes — they chain in insertion order
const post = new PosterizePass()
const vig = new VignettePass()
flatland.addPass(post).addPass(vig)
// Zero-cost parameter updates
post.bands = 10
vig.intensity = 0.25

Pass Chaining

Multiple passes compose into a single pipeline. Each pass receives the previous pass’s output:

flatland.addPass(posterize) // 1. Reduce color bands
flatland.addPass(lcdGrid) // 2. Apply LCD pixel grid
flatland.addPass(backlight) // 3. Add backlight bleed
flatland.addPass(vignette) // 4. Darken edges

Passes that only do color math (posterize, quantize, scanlines, vignette) merge into a single shader — TSL compiles the chain into one GPU program. Passes that call convertToTexture(input) internally (CRT curvature, VHS distortion, chromatic aberration) introduce an intermediate texture sample because they need to read at different UVs.

Dynamic Switching

Swap pass chains at runtime:

flatland.clearPasses()
// Apply a new preset
const crt = new CRTPass()
flatland.addPass(crt)

Pass Management API

MethodDescription
flatland.addPass(pass, order?)Add a pass (optional explicit order)
flatland.removePass(pass)Remove a specific pass
flatland.clearPasses()Remove all passes
flatland.passesRead-only array of current passes
pass.enabledToggle a pass without removing it

Effects That Need Texture Sampling

Some effects distort UVs — they read pixels at offset positions. These require convertToTexture(input) to create a sampable texture from the node graph:

import { convertToTexture } from 'three/tsl'
import { createPassEffect } from 'three-flatland'
import { vhsDistortion } from '@three-flatland/nodes'
const VHSPass = createPassEffect({
name: 'vhs',
schema: { time: 0, intensity: 0.012, noiseAmount: 0.05 },
pass: ({ uniforms }) => (input, uv) => {
const tex = convertToTexture(input)
return vhsDistortion(tex, uv, uniforms.time, uniforms.intensity, uniforms.noiseAmount)
},
})

Effects that need convertToTexture: crtComplete, crtCurvature, vhsDistortion, chromaticAberration.

Effects that work directly on the color node: posterize, quantize, scanlines, lcdGrid, crtVignette, lcdBacklightBleed, staticNoise.

Animating Parameters

Schema properties update the GPU uniform directly — no shader recompilation:

const vhs = new VHSPass()
flatland.addPass(vhs)
// In the render loop
vhs.time = elapsed // Drive distortion animation
vhs.intensity = 0.02 // Adjust strength

Available TSL Nodes

All post-processing nodes are exported from @three-flatland/nodes:

CRT Display

NodeDescription
crtCompleteFull CRT simulation (curvature + scanlines + bloom + vignette + color bleed)
crtCurvatureBarrel distortion for curved screen
crtVignetteEdge darkening
crtBloomGlow from bright pixels
crtColorBleedRGB channel offset
crtConvergenceRGB convergence offset

Scanlines

NodeDescription
scanlinesSmoothSine-wave scanline overlay
scanlinesHard scanline pattern
scanlinesGlowBright scanline edges
scanlinesInterlacedAlternating-field interlace

LCD Display

NodeDescription
lcdGridVisible pixel grid (handheld console)
lcdBacklightBleedUneven backlight glow
dotMatrixDot matrix display pattern
lcdPocketGame Boy-style LCD
lcdGBCGame Boy Color LCD

Retro Color

NodeDescription
posterizeReduce color bands
quantize8-bit color reduction
bayerDither4x4Ordered dithering
palettizeMap to a color palette

Analog Video

NodeDescription
vhsDistortionVHS tracking errors and wave distortion
staticNoiseAnalog TV snow
chromaticAberrationRGB channel separation
ntscCompositeNTSC signal artifacts
analogGlitchRandom glitch bands

Next Steps