ProjectsExperiments
Grid TSL
A procedural floor grid material for Three.js with WebGPU and TSL: primary lines, secondary lines and crosses, filtered so no moiré or flickering shows up, far away or at grazing angles.
- Status
- Active
- Period
- 2025.09 — present
- Type
- Library / package
- Role
- Solo project, Shaders, Performance optimisation
- Technologies
- Three.js
- WebGPU
- TSL
- Vite
- Links
- Demo — grid-tsl.vercel.app (opens in a new tab)
- Repository — github.com (opens in a new tab)
- Development log
- 1 entry
Context and goal
A floor grid is one of the first things in almost any 3D scene, and also one of the worst-looking when it is not properly filtered: far away the lines get thinner than a pixel, flicker as the camera moves and form moiré patterns.
The first version, from September 2025, was a material with two line layers and a cross layer, fixed presets and a triplanar variant. Version 2, from November 2026, is a rewrite from scratch: a single floor material, with every layer filtered, every parameter editable live and packaged to be used in other projects.
Interactive demo
Drag to orbit the camera and lower it to a grazing angle: far away the grid turns into an even tone, with no moiré. Add #debug to the URL to see the material's panel in the three.js Inspector, where every parameter can be edited and the setup copied as JS or JSON. It needs a browser with WebGPU.
How it is filtered
- Anisotropic pixel footprint: the position derivatives are computed once and per axis. At grazing angles, each direction is filtered according to what it actually covers on screen.
- Lines: with Ben Golus' pristine grid technique, the drawn width never drops below one pixel, but its intensity is scaled down by the same amount, so thin lines do not flicker. Once a cell covers less than about two pixels, the layer converges to its average coverage: far away you see an even tone instead of moiré.
- Crosses: each arm is a filtered line multiplied by a band with an analytic box filter, which preserves energy. The thickness and length of the arms also converge to their average.
- Compositing in linear space by each layer's coverage: background, secondary lines, crosses and primary lines.
As a package
It installs with npm and uses the three.js of the project that imports it, with no other dependencies:
import { GridMaterial } from 'grid-tsl'
const material = new GridMaterial({
primaryColor: '#5c6370',
secondarySpacing: 0.5,
fadeRadius: 80,
})
material.crossSize = 0.3 // every parameter can be changed live
grid-tsl: the material and its defaults.grid-tsl/debug: a panel in the three.js Inspector that edits the material and copies the parameters as JS or JSON. It is imported dynamically, so it stays out of the production bundle.grid-tsl/nodes: the standalone TSL functions, to build a material of your own.
To keep the shader light, derived values (reciprocals, ratios to the cell, fade radii) are computed on the CPU when a parameter changes, and the circular fade is only compiled in when it is enabled: when off, the material is opaque and carries neither that code nor blending.
Results and limitations
- The grid is projected onto the world XZ plane, so it only works on horizontal surfaces. Version 1's triplanar variant, for objects without UVs, is gone.
- Version 2 renamed the parameters and removed the presets: a project using version 1 has to be adapted. Version 1 is still available under the
v1.0.0tag. - It needs a browser with WebGPU.
Credits
- Ben Golus, The Best Darn Grid Shader (Yet), for the line filtering.
- Inigo Quilez, Filtering procedurals, for the crosses' box filter.
- Version 1 started from Bruno Simon's devlog Procedural Grid with TSL.
Development log
Grid TSL v2: what I removed and what got better
A rewrite from scratch: fewer variants and presets, and a grid with no moiré in any of its layers.

