▶ Live demo: run this tutorial — built from src/10_agents_on_the_globe.ts. Distilled from samples/geo_agents_ground.ts.
AgentScript has Leaflet and MapLibre integrations — agents on a real map. Taos Engine goes one step further: agents on real 3D terrain. This capstone runs a herd that walks the streamed Grand Canyon, using everything from the earlier tutorials — neighbor flocking, a patch field, per-agent state — plus the geo stack. Keyless (AWS Terrarium elevation + Esri imagery; no account).
The key insight#
A GeoScene's world frame is a local East-North-Up tangent plane (Y-up) at its
origin — which is exactly the flat XZ plane the agent sim already runs in. So
anchoring a GeoFrame at a lat/lon drops the whole model onto the planet with no
coordinate gymnastics. The sim still computes in local XZ; geo streams the terrain
under it. (Read geo tutorial 1 for the geo basics —
GeoFrame, GeoScene, attach.)
Composing the two stacks#
Geo renders deferred, so the agents (additive HDR arrows) go in just before tonemap.
The herd is a neighbor-flock (tutorial 4) authored over a bounce world:
deferredPreset({ sky: { kind: 'color', color: [0.62, 0.74, 0.92, 1] }, ao: 'ssao' })(engine);
const geo = new GeoScene(engine.ctx.device, GeoFrame.atLonLat(LON, LAT, ALTITUDE));
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: ESRI_WORLD_IMAGERY }); // keyless
geo.attach(engine, { cameraGO });
const particles = new ParticleFeature({ config: cfg, worldTransform });
engine.addFeatureBefore(particles, 'TonemapFeature');worldTransform keeps the flock locked to the globe as the floating origin
reanchors during camera flight:
const snap = geo.frame.snapshot();
const worldTransform = () => mat4dToEngine(geo.frame.rigidFromSnapshot(snap));Walking the terrain#
Three pieces make the herd follow the ground instead of flying:
A geo-referenced height field. Once terrain covers the anchor, sample its surface into a patch field —
GeoScene.sampleHeightGridreads the terrain over the local grid and returns curvature-corrected local-Y offsets:TypeScriptparticles.pass?.setPatchField(engine.ctx, HEIGHT_FIELD, geo.sampleHeightGrid([-HALF, -HALF], [WORLD, WORLD], GRID));Re-sample a few times as deeper LODs stream in.
Terrain-follow in the step. Each turtle sets its Y by sensing that field:
TypeScriptaction.set('position', expr.vec3( expr.swizzle(pos, 'x'), expr.add(agent.sense(HEIGHT_FIELD, 'bilinear'), SURFACE_OFFSET), // +small lift expr.swizzle(pos, 'z'), )),Surface tilt. The flat arrows lie on the slope by storing the terrain normal (central differences of the height field) in
velocityand settingrender.tiltToSurface: true. Because that repurposesvelocityas a render channel, setcfg.integrateVelocity = falseso the stored normal isn't treated as motion.
Together: a coherent flock draped on the canyon walls, depth-occluded by the ridges, that tracks the globe as you fly. The other geo agent samples in the repo do a flying flock and a 2D top-down map (the Leaflet/MapLibre look, via an orthographic camera over aerial imagery).
That's the series#
You've gone from turtles wandering a torus to a flock walking a real canyon, with the full AgentScript triad — turtles, patches, links — plus breeds, reproduction, measurement, a visual editor, and geo. The module guide is the reference for everything; the samples are the ground truth.