Poisson Disc
Poisson disc point sampling
Quick Start
Loading...
Source
Loading...
Documentation
Poisson Disc Sampling
GPU-accelerated generation of 2D points with a guaranteed minimum distance between them. Returns a GPUBuffer of vec2<f32> points. Useful for object placement, texture sampling, blue noise generation, and procedural content placement.
Usage
import { poissonDisc } from './poisson-disc';
const result = await poissonDisc(device, {
width: 100,
height: 100,
minDistance: 2.0,
seed: 42,
maxPoints: 10_000
});
const pointBuffer = result.points; // GPUBuffer of vec2<f32>
const count = result.count; // number of points generated
result.destroy();
API
poissonDisc(device, options?)
Returns a Promise<PoissonDiscResult>. This is a one-shot operation — call it, get the result, use the buffer.
| Option | Type | Default | Description |
|---|---|---|---|
width |
number |
100 |
Domain width |
height |
number |
100 |
Domain height |
minDistance |
number |
2.0 |
Minimum distance between points |
seed |
number |
(random) | RNG seed for reproducible results |
maxPoints |
number |
10_000 |
Upper bound for buffer allocation |
candidatesPerPoint |
number |
30 |
Candidates tested per active point per iteration |
maxIterations |
number |
500 |
Maximum iterations before stopping |
result.points
GPUBuffer containing vec2<f32> values (8 bytes per point). Usage: STORAGE | COPY_SRC | COPY_DST.
result.count
The number of points actually generated. Use this to know how much of the buffer is valid.
result.destroy()
Releases internal GPU buffers (grid, active list, counters). The points buffer is not destroyed — it's yours to use and destroy when done.
Algorithm
This implements a GPU-accelerated variant of Bridson's algorithm:
Grid setup — Divide the domain into cells of size
minDistance / sqrt(2). This guarantees each cell contains at most one point, enabling fast spatial lookup.Seed — Place an initial point at the domain center.
Iterate — For each active point, generate
candidatesPerPointrandom points in the annulus betweenminDistanceand2 * minDistance. For each candidate:- Check the 5×5 neighbourhood of grid cells for distance violations
- If valid, atomically claim the grid cell (compare-exchange) and add the point
- Add newly accepted points to the next iteration's active list
Converge — Repeat until no active points remain (all candidates were rejected).
The GPU parallelism comes from evaluating many active points and their candidates simultaneously. Atomic compare-exchange on the grid ensures correctness when multiple threads try to claim the same cell.
WGSL loading
The default import uses Vite's ?raw suffix:
import shaderSource from './poisson-disc.wgsl?raw';
If you're not using a bundler, load via fetch:
const shaderSource = await fetch(new URL('./poisson-disc.wgsl', import.meta.url)).then((r) =>
r.text()
);
Modifying
Extend to 3D
To generate 3D points:
- Change
vec2<f32>tovec3<f32>in points buffer and grid lookups - Use a 3D grid:
cell_size = minDistance / sqrt(3) - Sample candidates in a spherical shell instead of a 2D annulus
- Check a 5×5×5 neighbourhood instead of 5×5
Variable density
To vary the minimum distance across the domain:
- Pass a density texture as an additional binding
- In the
is_validfunction, sample the density at the candidate position and use it to modulatemin_distance - This lets you create denser sampling in some areas (e.g., near a character) and sparser in others
Use the output buffer for instanced rendering
The points buffer can be bound directly as a vertex buffer or storage buffer in a render pipeline:
@group(0) @binding(0) var<storage, read> points: array<vec2f>;
@vertex
fn vs(@builtin(instance_index) instance: u32) -> @builtin(position) vec4f {
let pos = points[instance];
// Transform to clip space...
}
Change the point data layout
To add extra data per point (radius, category, color), increase the point struct size and modify the points buffer layout. For example, change array<vec2f> to array<vec4f> where .xy is position, .z is radius, and .w is category.
Further Reading
Resources on Poisson disc sampling and spatial point generation.
Core Papers
Robert Bridson, "Fast Poisson Disk Sampling in Arbitrary Dimensions" (SIGGRAPH 2007) The foundational algorithm this module is based on. Introduces the dart-throwing approach with an acceleration grid, running in O(n) time. Short, elegant, and highly influential. https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
Daniel Dunbar, Greg Humphreys, "A Spatial Data Structure for Fast Poisson-Disk Sample Generation" (SIGGRAPH 2006) Proposes grid-based acceleration for Poisson disc sampling. The spatial grid approach used in this module draws from this work. https://dl.acm.org/doi/10.1145/1179352.1141915
GPU Implementations
Wei, "Parallel Poisson Disk Sampling" (SIGGRAPH 2008) The first parallel GPU algorithm for Poisson disc sampling. Uses a phase-based approach where points are generated in waves to avoid conflicts. https://dl.acm.org/doi/10.1145/1360612.1360619
Yuksel, "Sample Elimination for Generating Poisson Disk Sample Sets" (2015) An alternative approach: generate more points than needed (e.g., via uniform random), then iteratively remove points that are too close. Can be more GPU-friendly than iterative dart-throwing. https://dl.acm.org/doi/10.1111/cgf.12538
Blue Noise and Applications
Ulichney, "Dithering with Blue Noise" (1988) Poisson disc distributions are a form of blue noise — random but with no low-frequency content. This paper explains why blue noise is perceptually superior for sampling and dithering. https://ieeexplore.ieee.org/document/1457527
Pharr, Humphreys, "Physically Based Rendering" (PBRT), Chapter 7: Sampling Covers how Poisson disc and other blue noise distributions are used in ray tracing and path tracing for better convergence with fewer samples. https://pbr-book.org/4ed/Sampling_and_Reconstruction
Procedural Generation
de Carpentier, "Procedural Object Placement" Practical guide to using Poisson disc sampling for placing objects (trees, rocks, buildings) in game worlds. Discusses density maps, multi-class placement, and real-time generation. https://www.decarpentier.nl/scape-procedural-basics
Red Blob Games, "Poisson Disc Sampling" by Amit Patel An excellent interactive visual explanation of Bridson's algorithm with step-by-step animation. Great for building intuition. https://www.redblobgames.com/articles/noise/introduction.html
General References
- Jason Davies, "Poisson-Disc Sampling" (Interactive Demo) Beautiful interactive visualization of the algorithm generating points in real-time. Helpful for understanding the annulus sampling and grid check steps. https://www.jasondavies.com/poisson-disc/