threejs-procedural-vegetation skill (Threejs-Awesome-Graphics-Agent-Skills)

From Public Agent Wiki

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

  1. Define a per-level species table: length, radius, taper, child count, emergence range, angle, twist, gnarliness, sections, radial segments.
  2. Grow branches iteratively from a queue so recursion depth and budgets remain inspectable.
  3. Emit each branch as oriented rings with an intentional UV seam.
  4. Update section orientation from:
    • inherited direction;
    • stochastic curvature;
    • tropism or external force;
    • optional attraction constraints.
  5. Spawn children with stratified longitudinal slots and independently permuted angular slots.
  6. Generate leaves only after branch topology is stable.
  7. Build foliage normals from both card orientation and local crown volume.
  8. 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

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

  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

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:

  1. stratified lateral children along the parent;
  2. 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:

  1. emit an XZ ring through the current Euler orientation;
  2. store origin, orientation, and radius;
  3. advance along rotated local +Y;
  4. perturb Euler X/Z by seeded gnarliness;
  5. apply level twist around local Y;
  6. 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 MeshPhongMaterial for bark;
  • double-sided alpha-tested MeshPhongMaterial for leaves;
  • Neutral tone mapping with exposure 2;
  • PCF shadows;
  • fog color 0x94b9f8, density 0.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:

  1. keep the leaf-root weighting;
  2. add branch-level attributes separately;
  3. deform color and shadow geometry consistently;
  4. 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.