---
name: forest-trees
description: >
  Generate low-poly US forest trees in one Three.js file: one
  Trees.create call, season palettes, textured or faceted or card
  foliage, and vertex colours that only tint. Use when adding a species
  or changing season, detail or leaves on WebGPU. The reference file is
  trees.html beside this skill. If they disagree, the HTML wins.
---

# Forest trees

One HTML file. No bundler, no npm, no photo tiles. Three.js 0.186.1 from a CDN via an import map (`three` and `three/webgpu` both point at `three.webgpu.js`). `WebGPURenderer` only. Set `renderer._getFallback = null`. If `navigator.gpu` is missing, or the backend is not WebGPU after `init()`, stop. No WebGL fallback.

Open it through a server. `file://` will not load the ES modules. `php -S localhost:8000` or `npx serve` in the folder is enough.

This is the early Hard Road alpha set, shown as a gallery. Do not scatter it into a streaming forest unless the user asks.

## Create

`Trees.create(type, { seed, season, detail, leaves })` returns a `Group`.

- `season`: `summer`, `autumn`, `winter`, `dead`. Winter sets `snow`.
- `leaves`: `textured` (canvas atlas, `alphaTest`), `faceted` (icosahedra), `cards` (planes).
- `detail`: `low`, `med`, `high`, `ultra`. That table sets `cseg`, `bdet`, `cards` and `texRes`.

Types: spruce, fir, pine, whitepine, oak, broadleaf, birch, bush, shrub, deadtree, deadpine, stump, log. A new type needs a builder on `B` and a label in `NAMES`, or it builds with no name in the gallery.

Stumps have buttress roots. Logs do not. Do not add roots to a felled log.

## Colour

Material hexes are sRGB. Pass them to `THREE.Color` and stop. Do not call `convertSRGBToLinear`. On 0.186.1 the renderer already does that conversion, and doing it twice darkens the canopy.

Leaf canvases set `colorSpace = SRGBColorSpace`.

`tintGeo` writes vertex colours around white. They modulate the material colour. The palette stays on the material, so one material serves many meshes and a season change does not require a new geometry. `ensureColors` fills any mesh that has no colour attribute. A vertex-colour material with an empty channel reads garbage.

## Orbit

Phi is `pol`, measured from +Y.

```
x = target.x + dist * sin(pol) * sin(az)
y = target.y + dist * cos(pol)
z = target.z + dist * sin(pol) * cos(az)
```

Pointer move right decreases `az`. Labels are projected from world positions. Hide a label when `project()` reports `z > 1`.
