PhytoEngine · Docs

Integration docs

How to find assets, what they cost in triangles and bytes, and how to put them in a scene. If you just want to see the catalog, start at explore; if you need a plant that does not exist yet, that is the studio.

Quickstart

From nothing to an instanced forest

Three steps, and they are a real sequence — you cannot instance a plan you have not resolved. Hit the API for individual assets, or use the zero-dependency scene module to lay out a whole deterministic stand.

1

Find assets — by coordinate, by natural language, or by exact filter

Three ways in. /api/v1/at takes a latitude and longitude, works out what is there and what grows there, and hands back the trees the catalog has — for clients placing vegetation on real-world geography that know where but not what. /api/v1/query parses species, stage, climate, season and style out of plain text. Or filter precisely with /api/v1/assets. Every result is a permanently cacheable static .glb with tri_count, file_size_bytes and real height_meters.

GET
# you know where you are, not what grows there
GET /api/v1/at?lat=46.4900&lon=-80.9900&count=3
#   -> place: Greater Sudbury, Ontario, Canada — boreal forest
#   -> assets: sugar-maple, birch-scan, pine-leafy
#   -> gaps:   trees that grow there and we cannot serve yet

# easiest: let the query parser do the work
GET /api/v1/query?q=northern+ontario+old+growth+birch

# or take exact control (all filters optional, combinable)
GET /api/v1/assets?species=birch&stage=mature&climate=boreal&lod=1
2

Lay out a scene — deterministic, seed in / forest out

The scene module is a plain ES module (browser or Node, no dependencies). Same seed + biome always produce the byte-identical forest — so every client and every server agree without shipping placement data.

scene.mjs
import { generateForest, assetPlan } from
  'https://phytoengine.com/js/forest-recipe.js';

// deterministic layout: trees, understory, ground cover, a suggested camera
const recipe = generateForest({ seed: 7, biome: 'boreal-mix', size: 140 });

// resolve what the scene needs against the live catalog
// (limit defaults to 100 — ask for the whole catalog, or the plan reports false gaps)
// has_more: true means you got a page, not the catalog — the gaps would be lies
const { assets, total, has_more } = await (await fetch('https://phytoengine.com/api/v1/assets?limit=5000')).json();
const plan = assetPlan(recipe, assets);

console.log(plan.totals);  // { drawCalls, instancedTris, downloadBytes, ... }
console.log(plan.gaps);    // anything the catalog couldn't fill + what it substituted
3

Instance it — one draw call per bucket

The plan groups placements into buckets, one InstancedMesh each. Load each bucket's chosen-LOD file once, then stamp its instances. Apply scaleFix so a substituted species still reads at its real height.

instance.mjs — three.js
const loader = new GLTFLoader().setMeshoptDecoder(MeshoptDecoder);
const dummy  = new THREE.Object3D();

for (const b of plan.buckets) {
  const gltf = await loader.loadAsync(b.url);            // b.lod already picked per ring
  const src  = gltf.scene.getObjectByProperty('isMesh', true);
  const mesh = new THREE.InstancedMesh(src.geometry, src.material, b.instances.length);

  b.instances.forEach((it, i) => {
    dummy.position.set(it.x, it.y, it.z);                 // recipe coords are world coords — Y is up
    dummy.rotation.set(it.lean ?? 0, it.rotation, (it.lean ?? 0) * 0.7);  // yaw + slight lean
    dummy.scale.setScalar(it.scale * b.scaleFix);         // real-height correction
    dummy.updateMatrix();
    mesh.setMatrixAt(i, dummy.matrix);
  });
  scene.add(mesh);
}

That's a full, seeded, instanced stand. See it running → The reference client (preview.html) does exactly this, plus terrain, wind and grass.

Built for low-memory phones

A forest isn't a multi-gigabyte download.

You download a species, not a forest. A handful of small base meshes get GPU-instanced into thousands of plants; the layout comes from a seed, not a scene file; distant trees collapse to ~4-triangle billboards. It streams in bite-sized, cacheable pieces — so a living stand renders on a simple phone without ever holding gigabytes in memory.

500+
trees rendered
a few MB
total download
dozens
base files
dozens
draw calls

representative — open on the live server for exact, computed numbers

Instanced

Download a species, not a forest

One small base mesh becomes a whole stand via GPU instancing — thousands of plants, one draw call per bucket, and no per-tree geometry to ship or store.

Seeded

Layout is a number, not a file

The same seed and biome produce the byte-identical forest everywhere, so a scene costs you an integer over the wire instead of a placement table.

LOD 0–3

Distance pays for itself

Every asset ships a full decimation chain down to a near-billboard, so the trees you cannot make out stop costing what the ones in front of you do.

What you get

Built to drop straight into a real-time client

Everything is a static file and a plain ES module. No SDK, no runtime, no account — and nothing that assumes which engine you are in.

glTF

Loads in whatever you already use

meshopt-compressed GLB with WebP textures. three.js, Babylon, Godot, Unity, Bevy — if it reads glTF, it reads these.

Real scale

Metres, not arbitrary units

Every asset carries a measured height_meters and pivots at base centre, so a sapling next to an old pine is the right size without you tuning anything.

Wind ready

Colour channels carry the rig

Per-vertex colour holds the tint; its alpha holds wind stiffness. Drive foliage motion in a vertex shader with no bones and no extra attributes.

Any habit

Not only trees

Shrubs, cacti and succulents, grasses, ferns, flowers, vines, and invented flora. Growth habit is a real column, so a cactus is never handed a trunk and a crown.

Stages

Plants that have an age

Sapling through old, plus damaged, fallen, chopped, stump-cut, lightning-struck and rotting conditions — the states a world actually needs.

Fidelity 1–3

A rung, not a slider

Stylized, leaf-card and scan-grade are separately authored assets, each with its own LOD chain. Pick how a plant was made, independently of how far away it is.

In the catalog today

A living set of species, growth stages and props

Conifers with full sapling→old age ramps, storybook species, deadwood and understory. Browse and search the whole set in the explorer.