Bloom
Multi-level bloom post-processing
Quick Start
Loading...
Source
Loading...
Documentation
bloom
Multi-level bloom post-processing. Extracts bright regions, builds a mipmap pyramid with Jimenez 13-tap downsampling, applies separable Gaussian blur at each level, upsamples with tent filtering, and composites the result back onto the original image.
Quick Start
import { createBloom } from './gpu-modules/bloom/bloom';
const bloom = createBloom(device, { format: 'rgba8unorm', levels: 5 });
// Each frame — input and output are GPUTextures
bloom.render(sceneTexture, outputTexture, {
threshold: 0.8,
strength: 1.0,
radius: 0.5,
softKnee: 0.5
});
bloom.destroy();
API
createBloom(device, options?)
device—GPUDeviceoptions.format— Texture format. Default'rgba8unorm'.options.levels— Number of mip levels in the bloom pyramid. Default5.
Returns { render, destroy }.
render(input, output, params?)
Applies bloom from input texture to output texture. Both must have TEXTURE_BINDING usage; output must also have RENDER_ATTACHMENT.
params.threshold— Brightness threshold for bloom extraction. Default0.8.params.strength— Bloom intensity multiplier. Default1.0.params.radius— Blur radius scale. Default0.5.params.softKnee— Soft threshold transition width. Default0.5.
How It Works
- Threshold — Extract pixels brighter than
threshold(soft-knee smoothstep). - Downsample — Build a mip chain using Jimenez 13-tap filter (avoids aliasing artifacts).
- Blur — Apply 9-tap separable Gaussian blur (horizontal + vertical) at each mip level.
- Upsample — Walk back up the chain with a 9-tap tent filter, additively combining levels.
- Composite — Add the bloom result to the original image, scaled by
strength.
How to Modify
- Change quality vs performance: Reduce
levelsfor fewer blur passes (faster, less spread). Increase for wider, smoother bloom. - HDR pipeline: Change
formatto'rgba16float'for HDR bloom. The threshold and composite math already works with HDR values. - Custom threshold: Edit
fs_thresholdin the WGSL to use a different extraction method (e.g., per-channel threshold, color-based selection). - Anamorphic bloom: Set the blur direction to only blur horizontally (edit the direction uniforms) for a cinematic streak effect.
Further Reading
Rationale
Bloom simulates the way bright light bleeds into surrounding areas in cameras and the human eye. It's one of the most common post-processing effects in games and real-time graphics — any scene with HDR lighting, emissive materials, or bright light sources benefits from bloom. Without it, bright areas look flat and clipped.
This module implements a production-quality bloom pipeline used in shipped AAA games, in a single self-contained WebGPU file.
Original Research
Jorge Jimenez (2014) — Next Generation Post Processing in Call of Duty: Advanced Warfare — The presentation that introduced the 13-tap downsample filter used in this module. The key insight is using a weighted combination of 13 bilinear taps instead of a simple box filter, which prevents aliasing and fireflies in the bloom. https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare/
Kawase (2003) — Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L — An earlier approach using iterative dual-blur that's cheaper but lower quality. Good context for understanding the design space.
Existing Implementations
- Unity HDRP Bloom — Unity's built-in bloom uses the same Jimenez downsample approach.
- three.js UnrealBloomPass — Three.js implementation of multi-level bloom, good for cross-referencing. https://github.com/mrdoob/three.js/blob/dev/examples/jsm/postprocessing/UnrealBloomPass.js
Further Learning
- LearnOpenGL: Bloom — Step-by-step tutorial explaining the bloom pipeline with diagrams. https://learnopengl.com/Advanced-Lighting/Bloom
- Alexander Christensen: HDR Bloom — Practical guide to implementing bloom in a modern rendering pipeline, covers threshold, downsampling, and compositing.