threejs-procedural-vegetation skill (Threejs-Awesome-Graphics-Agent-Skills)
- Install
- SKILL.md (verbatim)
- Build sequence
- Visual failure conditions
- Routing boundary
- Other files in this skill
- assets/gpu-culled-flower-field/THIRDPARTYLICENSES.md (verbatim)
- Painted flower and grass atlases
- assets/structured-ash-growth/THIRDPARTYLICENSES.md (verbatim)
- references/gpu-culled-flower-field.md (verbatim)
- Contents
- Virtual address space and storage contract
- Ecology and deterministic identity
- Hierarchical visibility compaction
- Distance-tiered flower geometry
- Wind, contact, and ground coupling
- Frame and resource ownership
- Observed limits and defects
- Diagnostics
- references/structured-ash-growth-system.md (verbatim)
- Contents
- 1. Preserve the exact species table before tuning
- 2. Match the branch continuation model
- 3. Match section evolution
- 4. Match taper and child radius semantics
- 5. Match stratification and interpolation
- 6. Match ring and bark UV construction
- 7. Match leaf placement, card geometry, and normals
- 8. Match material and wind behavior accurately
- 9. Match composition before judging the generator
- 10. Required contract diagnostics
- 11. Numeric contract gate
What it does. Generate authored procedural trees, grass, ivy, flowers, and vegetation in Three.js or raw WebGPU. Use for surface-following vines, painted ivy paths, stylized or GPU grass, GPU-culled virtual flower fields, trunks, recursive branches, roots, canopies, leaf cards, species presets, deterministic growth, distance-tiered plant geometry, and rooted blade, stem, or petiole-hinge wind. Part of scottstts/Threejs-Awesome-Graphics-Agent-Skills (scottstts/Threejs-Awesome-Graphics-Agent-Skills).
| Upstream | scottstts/Threejs-Awesome-Graphics-Agent-Skills |
| Skill file | skills/threejs-procedural-vegetation/SKILL.md |
| License | MIT |
| Author | Scott Sun (scottstts) |
| Fetched | 2026-09-10 |
Install
npx skills add scottstts/Threejs-Awesome-Graphics-Agent-Skills --skill threejs-procedural-vegetation, or copy the skill folder into~/.claude/skills/threejs-procedural-vegetation/.- Raw file:
curl -sL https://raw.githubusercontent.com/scottstts/Threejs-Awesome-Graphics-Agent-Skills/HEAD/skills/threejs-procedural-vegetation/SKILL.md
SKILL.md (verbatim)
name: threejs-procedural-vegetation
description: Generate authored procedural trees, grass, ivy, flowers, and vegetation in Three.js or raw WebGPU. Use for surface-following vines, painted ivy paths, stylized or GPU grass, GPU-culled virtual flower fields, trunks, recursive branches, roots, canopies, leaf cards, species presets, deterministic growth, distance-tiered plant geometry, and rooted blade, stem, or petiole-hinge wind.
Procedural Vegetation
Represent a plant as a growth hierarchy plus rendering adaptations. Do not model it as randomly scattered cylinders.
This skill contains exemplary examples and assets beyond descriptive guidance, they're worth studying, referencing, or even copying. Use them sufficiently when relevant and do NOT blindly skip them.
Build sequence
- Define a per-level species table: length, radius, taper, child count, emergence range, angle, twist, gnarliness, sections, radial segments.
- Grow branches iteratively from a queue so recursion depth and budgets remain inspectable.
- Emit each branch as oriented rings with an intentional UV seam.
- Update section orientation from:
- inherited direction;
- stochastic curvature;
- tropism or external force;
- optional attraction constraints.
- Spawn children with stratified longitudinal slots and independently permuted angular slots.
- Generate leaves only after branch topology is stable.
- Build foliage normals from both card orientation and local crown volume.
- Choose wind scope explicitly. Leaf-root deformation, branch hierarchy deformation, and whole-tree sway are separate systems.
Read references/structured-ash-growth-system.md and preserve its preset, continuation, child-placement, leaf, material, wind, and composition contracts before tuning.
Read the Ash Growth System implementation with its authored preset for a contract-accurate implementation and its diagnostic attributes.
Read the stylized meadow grass implementation for authored blade-cluster geometry with a procedural fallback, image-driven path masking, per-instance origin/facing attributes, circular-arc rooted wind, gust fronts, tip flutter, color clumps, macro variation, translucency, and rim diagnostics.
Read the GPU-computed grass implementation for MRT blade-parameter generation, deterministic terrain-conforming placement, Voronoi clumps, Bezier blade folding, wind-facing yaw, distance LOD/culling, normal/color fading, translucency, and field diagnostics.
Read references/gpu-culled-flower-field.md for the exact virtual-address, ecology, hierarchical-compaction, indirect-draw, distance-tier, atlas, wind, contact, resource, and diagnostic contracts.
Read the GPU-culled flower-field entry and its complete raw WebGPU implementation for zero-record integer candidate reconstruction, 32 by 32 tile culling, three visible-ID streams, indirect near/middle/far draws, curved textured petals, identity-preserving horizon heads, and rooted moving-contact response.
Read the procedural surface ivy entry and its complete TypeScript implementation for seeded spline-following stems, repeated mesh reprojection, tangent-plane creep and droop, parallel-transport tube rings, growth reveal, instanced leaves and umbels, and rigid petiole-hinge wind. Treat the TypeScript modules as the only implementation; the entry file only re-exports them.
Visual failure conditions
- branches form visible helices;
- dense grass ignores terrain height or clump-level variation;
- every child emerges at the same relative height;
- bark texture scale changes with branch radius;
- leaves reveal flat card normals under rotation;
- leaf wind moves card roots instead of remaining anchored;
- branch wind is claimed to match a reference whose branches are static;
- different seeds change species identity rather than controlled variation;
- geometry cost grows without a per-level budget;
- surface-following stems are offset from the host or flip normals across seams;
- ivy branches ignore the tangent plane while attached;
- leaf wind rotates around the card center instead of the petiole.
- a million-flower field allocates CPU transforms or per-candidate records;
- distant flower LOD replaces species identity with one generic sprite;
- compaction and direct rendering reconstruct different roots or acceptance;
- flower heads stay world-up after their stems bend.
Routing boundary
Use $threejs-procedural-geometry for generic branch-ring emission without a
growth model. This skill owns species tables, vine and branch topology,
surface-following growth, foliage, grass fields, roots, and rooted wind.
Other files in this skill
- agents/openai.yaml
- assets/gpu-culled-flower-field/THIRD_PARTY_LICENSES.md
- assets/gpu-culled-flower-field/flower-petal-variants.png
- assets/gpu-culled-flower-field/painted-grass-atlas.png
- assets/structured-ash-growth/THIRD_PARTY_LICENSES.md
- assets/structured-ash-growth/ash.png
- assets/structured-ash-growth/bark-color.jpg
- assets/structured-ash-growth/bark-normal.jpg
- assets/structured-ash-growth/bark-roughness.jpg
- assets/stylized-meadow-grass/grass-blades-up.glb
- assets/stylized-meadow-grass/path.webp
- assets/stylized-meadow-grass/perlin.webp
- examples/gpu-computed-grass/gpu-grass-system.js
- examples/gpu-culled-flower-field/gpu-culled-flower-field.js
- examples/gpu-culled-flower-field/source/gpu-culled-flower-field.ts
- examples/procedural-surface-ivy/ivy-effect.js
- examples/procedural-surface-ivy/source/bvh.ts
- examples/procedural-surface-ivy/source/flowers.ts
- examples/procedural-surface-ivy/source/ivy.ts
- examples/procedural-surface-ivy/source/leafTexture.ts
- examples/procedural-surface-ivy/source/wind.ts
- examples/structured-ash-growth/ash-preset.js
- examples/structured-ash-growth/tree-system.js
- examples/stylized-meadow-grass/grass-system.js
- references/gpu-culled-flower-field.md
- references/structured-ash-growth-system.md
assets/gpu-culled-flower-field/THIRD_PARTY_LICENSES.md (verbatim)
Third-party licenses
Painted flower and grass atlases
Copyright (c) 2026 James and Inkwell contributors.
Licensed under the MIT License. Permission is granted, free of charge, to use, copy, modify, merge, publish, distribute, sublicense, and sell copies of the assets, subject to inclusion of the copyright and permission notice. The assets are provided without warranty.
assets/structured-ash-growth/THIRD_PARTY_LICENSES.md (verbatim)
Third-party asset notices
The Ash leaf alpha texture in this directory is a third-party asset used under the MIT License reproduced below.
The Bark001 color, normal, and roughness textures are AmbientCG assets dedicated to the public domain under the CC0 1.0 Universal dedication.
MIT License
Copyright (c) 2024 Daniel Greenheck
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
references/gpu-culled-flower-field.md (verbatim)
GPU-culled flower field
Use this reference for very large procedural flower populations whose candidate sites remain implicit, whose visible set is compacted on the GPU, and whose geometry changes representation with projected distance without losing species identity.
Contents
- Virtual address space and storage contract
- Ecology and deterministic identity
- Hierarchical visibility compaction
- Distance-tiered flower geometry
- Wind, contact, and ground coupling
- Frame and resource ownership
- Observed limits and defects
- Diagnostics
Virtual address space and storage contract
The gpu-culled-flower-field example addresses a square gridSize × gridSize
population by one u32 candidate ID. The hot path reconstructs the grid
coordinate and a wide stochastic jitter directly in WGSL:
gridX = candidateId % gridSize
gridZ = candidateId / gridSize
jitter = hashU32(candidateId * 11 + seed)
root.xz = latticeRoot + decodedJitter * spacing * 8.60
No matrix, position, species, colour, wind phase, or scale record exists per
candidate. The only population-sized allocations are three visible-ID buffers,
each storing one u32 per accepted flower. The implementation caps each tier at
16 MiB, for a 48 MiB maximum three-tier working set, and supports a maximum
2048 × 2048 = 4,194,304 virtual candidates on the baseline limits used by the
example.
The draw contract contains exactly eight indirect records, in this order:
near stem 60 near petals 528 near centre 48
mid stem 24 mid petals 132 mid centre 18
far stem 6 far head 6
Ecology and deterministic identity
The lattice is only an address space. Density comes from three rotated
value-noise octaves with weights 0.56, 0.29, and 0.15; octave frequency
multipliers are 2.07 and 1.91. Rotating each octave prevents a visible
axis-aligned interpolation grid.
The meadow probability is:
broad = organicField(root.xz, 0.024, 307)
detail = organicField(root.xz, 0.071, 401)
patch = mix(0.10, 0.92, smoothstep(0.31, 0.73,
broad * 0.78 + detail * 0.22))
keep = density * patch * distanceDensity
Species selection is independent per candidate. The exact cumulative cutoffs
for the eight species are 0.25, 0.41, 0.45, 0.56, 0.74, 0.89, and
0.93. A second hash selects one of five dominant petal variants. Ecology
controls presence; it never paints large-scale species bands.
Hierarchical visibility compaction
The hierarchy uses 32 × 32 = 1024 candidates per tile and a compute
workgroup size of 256. One reset pass clears indirect counts. A tile pass
tests a conservative tile centre/radius against distance and clip bounds, then
atomically appends surviving tile IDs while incrementing an indirect dispatch
count.
The candidate pass launches indirectly: one workgroup per surviving tile, four
batches of 256 lanes, and one candidate per lane per batch. Accepted IDs are
atomically appended into near, middle, or far storage according to 24 m and
52 m tier boundaries in the default profile. A one-workgroup finalize pass
fans each tier count out to every draw record that consumes the tier.
Flat compaction is a diagnostic/reference path. It runs the same acceptance and tier functions over every candidate, so differences isolate hierarchy and dispatch ownership rather than ecology or geometry.
Distance-tiered flower geometry
Near stems use two crossed ribbons with five longitudinal segments. Near
petals use a 4 × 2 curved grid per petal and analytic derivatives for the
normal. Middle stems use two longitudinal segments; middle petals use a 2 × 1
grid with the same species and dominant variant.
Petal count is species-dependent: the base counts are 5, 6, 8, or 9,
then the dominant variant adds 0, 1, or 2. When mixed variants are
enabled, 62% of slots keep the dominant variant and the remainder split
between a compatibility pair. The atlas has 8 species columns and 5
variant rows.
Far flowers retain a rooted stem plus a six-vertex procedural head. The head reconstructs species, scale, petal count, variant, and colour. It does not collapse the field to one generic billboard colour.
Wind, contact, and ground coupling
Stem position combines authored lean, two-frequency wind, and moving contact:
static lean ∝ along^curvePower
wind ∝ (sin(1.1t + phase) + 0.32 sin(0.63t + 0.4phase)) along²
contact bend ∝ influence along²
Roots therefore remain fixed. Flower-head orientation comes from the terminal
stem tangent sampled at along = 0.92 and 1.0; heads follow bent stems rather
than remaining world-up.
The painted grass atlas is part of the colour contract for ground and stems.
It has 3 × 2 tiles, mirrored repeat coordinates, and explicit mip sampling
for stem palette matching. Removing it changes the field identity, not merely
the inspection stage.
Frame and resource ownership
The reusable system owns the WebGPU device, canvas context, shader modules, pipelines, bind groups, visible-ID buffers, indirect records, depth texture, timestamp queries, and readback buffers. Its caller supplies each frame's view-projection matrix, camera position, elapsed seconds, pixel size, and contact state. The field compares the supplied view-projection against the last compacted frame and automatically marks culling dirty when the camera or projection changes.
Resize destroys and recreates only the depth texture. Grid-size changes rebuild the three visible buffers, tile buffer, and dependent bind groups. Disposal destroys every owned GPU texture, buffer, and query set.
Observed limits and defects
- The system requires WebGPU storage buffers, compute, indirect dispatch, and indirect draws; it has no WebGL fallback.
- Each visible tier reserves for the full candidate count. This is bounded but intentionally trades memory for overflow-free atomics.
- Tile rejection is conservative. It reduces tests but cannot provide exact occlusion culling.
- Ground relief is excluded from candidate-root reconstruction. Introducing terrain displacement requires the ground and flower roots to share one callable height field.
- The far tier preserves identity but not atlas alpha detail or curved petals.
- A split CPU/GPU placement stack is a defect: it removes the zero-record contract and makes culling and drawing disagree about identity.
Diagnostics
Use the four gallery modes as contract checks:
Hierarchical compact tile pass + indirect candidate pass + tiered draws
Direct reference every candidate executes the same acceptance functions
Flat compaction every candidate is compacted without the tile hierarchy
One petal variant compatibility mixing disabled, species shape retained
Read back near/middle/far counts, surviving tile count, candidate tests, four-byte ID memory, frame percentiles, submit time, and GPU render/cull time. If direct and compact paths disagree visually, inspect root reconstruction, acceptance, and draw-count fan-out before tuning culling margins.
references/structured-ash-growth-system.md (verbatim)
Structured Ash growth system
Use this reference when the target is a natural deciduous Ash with a stable species identity. Preserve the species table, continuation model, branch geometry, foliage, rooted wind, and composition contracts before tuning.
Contents
- Preserve the exact species table before tuning
- Match the branch continuation model
- Match section evolution
- Match taper and child radius semantics
- Match stratification and interpolation
- Match ring and bark UV construction
- Match leaf placement, card geometry, and normals
- Match material and wind behavior accurately
- Match composition before judging the generator
- Required contract diagnostics
- Numeric contract gate
1. Preserve the exact species table before tuning
The Ash Medium species contract is:
| Level | Length | Base radius factor | Sections | Radial segments | Child angle | Children | Child start | Gnarliness | Twist |
|---|---|---|---|---|---|---|---|---|---|
| 0 trunk | 43.47 | 2.00 | 12 | 12 | — | 7 | — | 0.03 | 0.09 |
| 1 primary | 27.14 | 0.63 | 8 | 6 | 48° | 4 | 0.23 | 0.25 | -0.07 |
| 2 secondary | 9.51 | 0.76 | 6 | 4 | 75° | 3 | 0.33 | 0.20 | 0 |
| 3 terminal | 4.60 | 0.70 | 4 | 3 | 60° | 0 | 0 | 0.09 | 0 |
All levels use taper 0.7. Growth force points up with strength 0.01.
Leaves:
type: ash
count per terminal branch: 16
start: 0
angle: 55°
double perpendicular cards
size: 2.67
size variance: ±0.72
alpha test: 0.5
rounded normals: enabled
Do not replace this table with guessed “tree-like” ranges before the contract is reproduced. Species identity is encoded in the uneven angles, lengths, starts, and child counts.
2. Match the branch continuation model
The generator creates two types of descendants:
- stratified lateral children along the parent;
- one terminal continuation branch from the parent tip for every deciduous level until the final level.
That continuation is essential to the sparse, irregular crown. A generator that creates only lateral children produces a candelabra or clipped crown.
The terminal continuation inherits:
origin = final parent section origin
orientation = final parent section orientation
radius = final parent section radius
level = parent level + 1
sections and radial segments = parent values
length = next-level species length
The inherited section/segment counts differ from ordinary lateral children, which use the next level's table values.
3. Match section evolution
The section-length contract, implemented in tree-system.js, is:
sectionLength = branchLength / sectionCount
Do not add an extra per-level divisor such as /(branchLevels - 1); the
species table is calibrated against the formula above. The complete build
produces branch bounds reaching roughly y=80.30 and leaf bounds reaching
y=83.69. Validate against the numeric gate in section 11 rather than
inferring height from any single line.
At every section:
- emit an XZ ring through the current Euler orientation;
- store origin, orientation, and radius;
- advance along rotated local +Y;
- perturb Euler X/Z by seeded gnarliness;
- apply level twist around local Y;
- rotate the section toward world growth force.
Gnarliness amplification:
effectiveGnarliness =
max(1, 1 / sqrt(sectionRadius))
* levelGnarliness
The force angular step is:
forceStrength / sectionRadius
clamped to the full angle between current local up and the target direction. Thin branches therefore respond more strongly than the trunk.
4. Match taper and child radius semantics
Deciduous taper:
sectionRadius =
branchStartRadius
* (1 - levelTaper * sectionIndex / sectionCount)
The final section of the final level collapses to a near-zero radius.
Lateral child radius is not simply the species radius:
childRadius =
levelRadiusFactor
* interpolatedParentSectionRadius
This couples the child thickness to emergence height and parent taper.
5. Match stratification and interpolation
For both lateral children and leaves:
along =
start
+ (slotIndex + seededJitter)
* ((1 - start) / count)
Independently shuffle angular slot IDs with the same seeded RNG:
azimuth =
2π
* (radialOffset + (permutedSlot + jitter[-0.5, 0.5]) / count)
Interpolate between adjacent stored sections:
- origin: linear interpolation;
- radius: linear interpolation;
- orientation: interpolation starts from section B and slerps toward
section A by
alpha, reversing the usual A→B expectation.
Then compose:
parent orientation
× azimuth around local Y
× emergence angle around local X
Do not derive child orientation from a newly constructed tangent frame; that changes the characteristic branch roll and twist.
6. Match ring and bark UV construction
Each section emits radialSegments + 1 vertices by duplicating the first radial vertex at the seam.
Choose one integer circumference wrap count for the entire branch:
wrapsX = max(1, round(branchStartRadius * barkTextureScaleX))
u = radialIndex / radialSegments * wrapsX
v = sectionIndex is even ? 0 : 1
The texture's runtime Y repeat is 1 / barkTextureScaleY.
This is not a real-distance longitudinal UV. If adapting the visual exactly, retain the alternating ring V pattern. If improving it, record the change as an intentional divergence and re-evaluate bark scale across trunk and twigs.
7. Match leaf placement, card geometry, and normals
Leaves are emitted along every final-level branch, not in synthetic clusters at branch tips.
Each leaf is a square card extending from local base y=0 to tip y=L, with width W. The double-card mode emits a second card rotated 90° around local Y.
Rounded vertex normal:
normalize(cardNormal + (vertexPosition - leafOrigin))
Use the same unrotated card normal for both perpendicular cards before adding the vertex direction. Preserve that quirk when reproducing the contract. A corrected per-card normal is a legitimate extension but changes canopy lighting and must be documented.
Use the included ash.png alpha silhouette. Replacing it with an ellipse or
analytic lozenge changes crown porosity and edge frequency enough to invalidate
visual comparison.
8. Match material and wind behavior accurately
The complete scene uses:
- textured
MeshPhongMaterialfor bark; - double-sided alpha-tested
MeshPhongMaterialfor leaves; - Neutral tone mapping with exposure
2; - PCF shadows;
- fog color
0x94b9f8, density0.0015; - a daylight sky gradient and directional sun.
The wind shader deforms leaf vertices only:
windPhase = 2π * simplex3(position / 70)
wind =
0.5 * sin(t * 0.5 + phase)
+ 0.3 * sin(2t * 0.5 + 1.3phase)
+ 0.2 * sin(5t * 0.5 + 1.5phase)
displacement = leafUvY * windStrength * wind
leafUvY roots the card base and moves the tip. The demonstrated branch
geometry is static; do not describe this mechanism as hierarchy-weighted
trunk/branch wind.
If extending it with branch motion:
- keep the leaf-root weighting;
- add branch-level attributes separately;
- deform color and shadow geometry consistently;
- label the result as an extension to the contract.
9. Match composition before judging the generator
Present the tree in a complete environment:
startup camera: (100, 20, 0)
target: (0, 25, 0)
horizontal camera constraint near the horizon
foreground tree at origin
100 background trees using radius = 175 + random * 500 (effective 175–675)
procedural grass/dirt ground
5,000 visible grass instances
flowers and rocks
blue atmospheric fog
daylight sky and sun
The exact startup camera clips the leaf bound slightly because its
upper vertical coverage is approximately y=82.7 while the leaf maximum is
approximately y=83.69. For a fixed 3:2 evaluation frame, move the camera
along the same target ray to approximately x=115; do not alter the tree to
solve a framing problem.
A black-background isolated tree is not a valid quality test. It removes foliage edge contrast, atmospheric depth, ground contact, and scale cues.
10. Required contract diagnostics
Capture:
final composition
branch-level colors
lateral children versus terminal continuations
child longitudinal slot IDs
child angular slot IDs
leaf origins along final branches
card normals versus rounded normals
bark UV checker
wind displacement magnitude
foreground bounds and camera frustum
Report:
branch jobs by level
terminal continuations by level
lateral children by level
leaf cards
vertices and triangles
seed
preset name
intentional divergences from the growth contract
11. Numeric contract gate
For the Ash Medium contract, assert:
branch vertices: 6,639
branch triangles: 9,120
leaf vertices: 21,760
leaf triangles: 10,880
branch bounds max Y: approximately 80.2981
leaf bounds max Y: approximately 83.6902
Matching only counts is insufficient; the earlier half-height implementation matched all counts while violating runtime section-length behavior.
Back to scottstts/Threejs-Awesome-Graphics-Agent-Skills or Agent skills.