---
title: "Restyling someone's WebGL terrain into night mode"
description: "mesq shipped a gorgeous open-source infinite-terrain scene in r3f. I didn't rebuild it — I reskinned it. A new theme palette and moonlit lighting turn a sunny field into a midnight one. Same shaders, different mood."
date: "2026-05-30"
tags: ["three", "webgl", "r3f", "frontend"]
---

[mesq](https://x.com/mesqme/status/2020672277055197234) open-sourced an
[infinite-terrain scene](https://github.com/mesqme/infinite-terrain) (MIT) that
stopped my scroll: endless procedural grass, streamed trees, wind flow lines,
and a little physics ball you roll around to explore it. I wanted my own take,
so instead of rebuilding it, I reskinned it for night. Click in and roll around
(WASD / arrows):

## The cheapest way to make something yours: a theme

The smart thing mesq did was build the scene around a theme object. Every color
the scene uses (terrain, sky, grass base and tip, the ball, leaves, the fresnel
rim on foliage) comes from one palette, and there's a switcher that swaps day
for a warmer "light" set. That one decision is what made my job easy.

I added a `night` palette next to the existing ones:

```js
night: {
  terrain: '#2b3c63',        // cool slate-blue ground
  background: '#0b1126',     // deep indigo sky
  grassBase: '#1d4a52',      // dark teal roots
  grassTop: '#57b291',       // moonlit green tips
  ball: '#9a7bff',           // a glowing violet you
  leaves: '#356a58',         // dim foliage
  bushFresnelColor: '#a6d4ff', // cold blue rim light
}
```

Then I dropped the lighting from a bright sun to a low, cool "moonlight": a
faint blue directional plus a blue hemisphere fill, so nothing reads as warm.
The shaders didn't change at all. They were already driven by those uniforms, so
recoloring the inputs recolors the whole world. Day became midnight by editing a
handful of hex values and three lights.

Once one new mood was that cheap, I made a few more. Each preset now bundles its
palette *and* a lighting rig, so switching it changes the whole feel: **aurora**
(polar green-cyan), **synthwave** (neon magenta + cyan), **sakura** (a bright
pink-blossom day), **ember** (volcanic dusk), and **noir** (moonlit grayscale).
The picker in the demo above swaps between them live, and you can toggle the
trees and stones while you're there.

## Getting it into the site

This is the heaviest thing on here by far. It pulls in three.js, react-three-
fiber, drei, rapier physics, and a stack of GLSL shaders. Two things made it
behave:

- **Client-only.** The site server-renders on Cloudflare Workers, where WebGL
  and physics don't exist. So the scene is loaded with a dynamic `import()`
  inside an effect, which keeps three and rapier out of the server bundle
  entirely. The page ships a tiny placeholder and swaps in the real scene once
  it's on the client.
- **GLSL as modules.** A Vite plugin lets the shaders import as strings with
  their `#include` directives resolved, so the materials port over untouched.

The infinite part is the nicest trick: it streams a 3×3 grid of chunks around
the ball and rescales the field at its border, so you can roll forever and never
hit an edge. I left all of that intact.

Full credit to [mesq](https://mesq.me/infinite-terrain) — the scene is his
(MIT), I just turned out the lights. Grab my night build on the
[Infinite Terrain](/components/infinite-terrain) page.
