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
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.0 tag.
  • It needs a browser with WebGPU.

Credits

LOG

Development log

  1. 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.