← All notes

Three.js

Low-poly forest trees with seasons in Three.js

This is the tree set from the early alpha of Hard Road. A gallery, not a forest. Spruce, fir, pine, white pine, oak, broadleaf, birch, a bush, a shrub, a dead oak, a dead pine, stumps and a row of felled logs.

This is one HTML file on Three.js 0.186.1 and WebGPU. It was written against Three r128. The generator is the same function. I use AI to write a lot of the species. Which trees get buttress roots, and that a felled log does not grow them, come from 25+ years of JavaScript, game development, modding and level design.

One call

Trees.create(type, { season, seed, leaves }) returns a group. Season picks the palette: summer, autumn, winter, dead. Winter sets snow. Foliage is one of three things. Textured paints a leaf atlas on a canvas and cuts it with alphaTest. Faceted builds icosahedra and tints the triangles. Cards are the cheap version, a handful of planes. Detail changes the segment counts, from low to ultra. New seed rebuilds the stumps and the logs along with the trees.

Vertex colour modulates the material colour. It hovers around white, so the palette stays in charge and one material can serve a lot of meshes. The underside is darker, from how high the vertex sits in its own geometry. A mesh with no colour attribute gets a quiet grain, because a vertex-colour material will read garbage from an empty channel.

On this version of Three

The r128 file converted every hex to linear by hand, and it told the canvas texture it was sRGB. On 0.186.1 the renderer already treats material colours as sRGB, so this copy passes the hex through and sets colorSpace on the leaf texture. Convert them again and the canopy goes dark.

Drag right and the row turns with your hand. The camera uses sine of azimuth on x and cosine on z, and azimuth decreases as the pointer moves right. The labels are projected each frame. Anything behind the camera is hidden.

The Hard Road alpha set. Drag to orbit, scroll to zoom. Season, foliage and detail rebuild the row.

The frame is that file. The names under the trees are just labels. They are not colliders, and they are not a UI you can click.

WebGPU only, on Three.js 0.186.1, with no WebGL fallback. If the browser has no WebGPU, the page stops. Open it through a server, because file:// will not load the modules. php -S localhost:8000 or npx serve in the folder is enough.

If you add a species, add it to Trees.create and to the name list, or it will build and never get a label. And leave the vertex colours around white. Painting the palette into the attribute means every season needs its own material.