GPU foliage wind and continuous emitters — engine22, batches11–12 Status: native acceptance and Chrome checks passed; engine22 deployed 2026-11-02. Combined set: Wind Through the Meadow, with Grass / Leaves / Rain / Sparks. Correction: finite leaf-tip geometry verified in Chrome after incremental rebuild. Bend foliage on the GPU while its roots stay fixed. createGPUInstances("grass", "blade", 24000, {size:1, extent:100}) styleGPUInstances("grass", {roughness:.8, windEnabled:true, windStrength:.15, windDirection:{x:1,y:0,z:.3}, windGust:.6, windFrequency:1, windFlutter:.02, windBase:0, windHeight:1}) Initialize positions/colors and optional style rotations/scales with compileGPU and stepGPU as usual. New built-in meshes: card (subdivided rectangle), blade (tapered grass), leaf (pointed oval with central ridge). All use local Y0..1; card/blade/leaf are world-oriented meshes, not camera-facing billboards. Custom readable static meshes also support wind. Set windBase and windHeight to that mesh's LOCAL root height and height span. The deformation follows squared normalized height; no skeleton or automatically detected branch hierarchy. Change wind without recompiling the compute program. setGPUWind("grass", {windStrength:.3, windDirection:{x:-1,y:0,z:0}}) setGPUWind("grass", {windPaused:true}) setGPUWind("grass", {windTime:0, windPaused:false}) setGPUWind("grass", {windEnabled:false}) setGPUWind patches styleGPUInstances settings. Strength/flutter are displacement metres; frequency is radians/second, not a physical wind-speed or stiffness solver. Gust0..1; strength0..20m; flutter0..2m. A horizontal direction is required. Wind uses per-instance phase and two time scales. Roots remain fixed. Normals, tangents, forward/depth/shadow passes and culling use the deformation. Actual instance positions and physics colliders do not bend. This is visual vegetation. Wind clock follows rendering delta, capped .1s/frame; separate from compute clock. Pause with windPaused; pauseGPU alone does not stop renderer wind. Continuously emit particles with fixed capacity and deterministic recycling. createBillboards("sparks", 1024, {size:.02, extent:100}) styleGPUInstances("sparks", {lit:false, surface:"additive", castShadows:false, streakSeconds:.025, streakLimit:.4}) createGPUEmitter("sparks", {rate:180, lifeMin:1.5, lifeMax:3, seed:1, position:{x:0,y:.4,z:0}, direction:{x:0,y:1,z:0}, speed:8, spread:.8, gravity:{x:0,y:-9.81,z:0}, wind:{x:1,y:0,z:0}, drag:.25, sizeStart:.025, sizeEnd:.002, colorStart:{x:1,y:.65,z:.16}, colorEnd:{x:.35,y:.045,z:.008}, floorEnabled:true, floor:0, bounce:.4}) startGPUEmitter("sparks") Await creation/setup calls when subsequent work depends on their completion. Use mesh "leaf" for spinning leaves; style lit opaque/cutout as appropriate. A collection must be styled before adding the emitter. The emitter owns both particle and style buffers. Runtime WGSL on that collection is stopped and further compute writes are rejected until removeGPUEmitter releases ownership. No per-particle JavaScript loop or GPU readback drives this system. Control and inspect an emitter. pauseGPUEmitter("sparks") stepGPUEmitter("sparks") resetGPUEmitter("sparks") startGPUEmitter("sparks") getGPUEmitterState("sparks") Reset clears all particles and resets seed/clock; it returns PAUSED. Step requires paused state. State.emitter includes settings, running, capacity, simulatedTime, steps, droppedSteps, dt and collisionMode. measureGPUInstance can inspect one particle from the same underlying buffer. Its properties are age, lifetime, random phase, birth time. Use emitter clock, not getGPUState's WGSL clock. Change settings only while paused; a successful change resets the emitter. pauseGPUEmitter("sparks") setGPUEmitter("sparks", {rate:250, wind:{x:2,y:0,z:0}}) startGPUEmitter("sparks") Capacity >= rate * lifeMax; rejected changes retain existing settings/state. Deterministic slots recycle on capacity/rate periods. Short-lived particles can leave empty slots until their next scheduled birth. No growing allocation. box is full emission-box size; spread0..1 is cone radius relative to direction. spin is radians/second. Size is the instance scale; billboard half-width, not necessarily the same bounding radius for every custom mesh. Colors are linear shader RGB multipliers, optional HDR0..20; material settings still apply. Drag is linear damping relative to wind, in inverse seconds. GPU stepping uses 1/60s, max4 steps/render frame; excess wall time is counted as dropped steps. Exact constant-gravity/linear-drag integration is used between plane contacts. This is a visual particle simulation, not AVBD/IPBF or a combustion model. Short velocity streaks, rather than a history of positions. styleGPUInstances("sparks", {streakSeconds:.02, streakLimit:.4}) Only camera-facing billboards use streaks. Direction is projected velocity; length is bounded speed*exposure, preserving the head location. Zero exposure restores regular billboards. worldCards:true makes ordinary billboards use world orientation instead of camera facing. Persistent curved trails/ribbons, flipbook atlases and emission from arbitrary surfaces remain future work. Collision scope floorEnabled adds a prescribed horizontal plane, with restitution and size-based clearance. It is ONE-WAY and does not push CS objects. No arbitrary Unity-object collision is silently added. Use existing addGPUCollider/addGPUSceneCollider with the separate typed particle-physics solver when actual scene coupling is needed; that solver cannot directly own this emitter's interleaved particle buffer. Do not promise firework bursts moving heavy rigidbodies from these visual sparks. Cleanup/share removeGPUEmitter removes only the simulation and retains the rendered collection. removeGPUInstances or clearRun releases the collection, styles and emitter. Store construction, settings, UI and commands in the editor script. One activity can offer Grass / Leaves / Rain / Sparks to compare the complete capability set. Four starting presets (each also includes that selector): gpu-wind-meadow.js, gpu-wind-leaves.js, gpu-wind-rain.js, gpu-wind-sparks.js. Capability flags: gpuFoliageWind, gpuContinuousEmitters, gpuVelocityStreaks. Refresh the Game iframe for engine22; Run alone does not update the engine. Reference approach: NVIDIA GPU Gems3 chapters6 and16, procedural wind/main bending and detail motion. This implementation adds no shader keywords/variants. https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-16-vegetation-procedural-animation-and-shading-crysis https://developer.nvidia.com/gpugems/gpugems3/part-i-geometry/chapter-6-gpu-generated-procedural-wind-animations-trees