Overview
The@hyperscape/shared package is the core Hyperscape engine, providing:
- Entity Component System (ECS)
- Three.js WebGPU rendering (WebGPU required - no WebGL fallback)
- PhysX physics simulation
- Real-time networking
- React UI components
- Game data manifests
Package Location
Terrain Generation System
The shared package includes a sophisticated procedural terrain generation system with multi-threaded performance and cached height lookups for optimal performance.Terrain Height Cache System
The terrain system uses a tile-based cache for fast height queries without re-evaluating noise functions. Critical bugs in the cache indexing were fixed in commit 21e0860 (Feb 25, 2026). Cache Structure:-
Tile Index Calculation Error:
- Issue:
getHeightAtCachedusedMath.floor(worldX/TILE_SIZE)which doesn’t account for PlaneGeometry’s centered coordinates - Impact: Terrain tiles were indexed incorrectly, causing ~50m vertical offset in height queries
- Fix: Added
worldToTerrainTileIndex()canonical helper - Formula:
- Result: Terrain height queries now return correct values for all world positions
- Issue:
-
Grid Index Formula Error:
- Issue: Grid index formula omitted the
halfSizeoffset from PlaneGeometry’s [-50,+50] range - Impact: Local coordinates within tiles were mapped incorrectly to height data arrays
- Fix: Added
localToGridIndex()canonical helper - Formula:
- Result: Height data lookups within tiles now use correct array indices
- Issue: Grid index formula omitted the
-
Color Cache Key Typo:
- Issue:
getTerrainColorAt()used comma separator (tileKey_x,z) instead of underscore (tileKey_x_z) - Impact: Color cache lookups always failed, forcing expensive recomputation
- Fix: Corrected key format to match height cache convention
- Result: Color cache now works correctly, improving terrain rendering performance
- Issue:
- Eliminates 50m vertical offset in player spawning and entity placement
- Fixes vegetation placement using incorrect terrain heights
- Prevents height query inconsistencies between cached and real-time lookups
- Improves terrain color rendering performance via working color cache
TerrainHeightParams.ts
Single source of truth for all terrain generation parameters. BothTerrainSystem (main thread) and TerrainWorker (web worker) consume these values to ensure identical terrain generation:
- Prevents parameter drift between main thread and workers
- TypeScript constants injected into worker code at runtime
- Changing a constant automatically updates both threads
- Includes noise layers, island mask, pond, coastline, and mountain boost
Web Worker Terrain Generation
Terrain generation offloaded to web workers for parallel processing:- Worker Height Computation: Full height calculation including shoreline adjustment
- Worker Normal Computation: Normals computed via (resolution+2)² overflow grid
- Performance: 63x reduction in main-thread noise evaluations
- Conditional Fallback: Main-thread recomputation only for tiles with flat zones (buildings/stations)
- Parallel Processing: Utilizes multiple CPU cores while keeping main thread free
GPU-Instanced Rendering Systems
Tree Dissolve Transparency System (March 2026)
New Feature (PR #1101): Depleted trees use screen-door dithering to become ~70% transparent instantly on depletion, then animate back to full opacity over 0.3s on respawn.
packages/shared/src/systems/shared/world/DissolveAnimation.ts
The dissolve system provides visual feedback for resource depletion and respawn using GPU-driven animations:
- Dual Encoding: InstancedMesh uses
instanceDissolveattribute, BatchedMesh uses batch color blue channel - Screen-Door Dithering: Uses Bayer 4×4 pattern in
alphaTestNodeto keep trees in opaque render pass - LOD Preservation: Dissolve state carries over during LOD transitions (no visual pops)
- Interrupt Handling: Continues from current progress on direction reversal (no pops)
- Zero Allocation: Reuses module-level
_completedarray to avoid per-frame allocation
GPU_VEG_CONFIG):
- No alpha blending overhead (opaque render pass)
- Full early-Z rejection
- No transparency sorting required
- Consistent performance regardless of dissolve state
GLBTreeInstancer (commit 0871acb)
InstancedMesh-based tree rendering system that replaces per-treescene.clone(true) with shared InstancedMesh pools per LOD level.
Performance Impact:
- Eliminated per-tree geometry cloning (saves ~2-5ms per tree spawn)
- Reduced draw calls from N trees to 3 (one per LOD level)
- FPS improvement: ~15-20% in dense forest areas
- Memory savings: ~80% reduction in geometry buffer allocations
- Initialized in
createClientWorld.tswhen stage scene is ready - ResourceEntity routes GLB trees through instancer instead of scene.clone()
- Handles depleted/stump/destroy lifecycle via no-op-safe calls
ResourceEntity Visual Strategy Pattern (commit bc60264)
Refactored ResourceEntity (~1700 lines removed) into delegated visual strategies using the Strategy Pattern. Visual Strategies:-
TreeGLBVisualStrategy: GLB tree models with LOD support
- Uses GLBTreeInstancer for instanced rendering
- Handles LOD transitions based on camera distance
- Manages stump state transitions
-
TreeProcgenVisualStrategy: Procedurally generated trees
- Uses ProcgenTreeInstancer for procedural geometry
- Supports runtime tree generation with L-system parameters
- Handles leaf/branch color variations
-
StandardModelVisualStrategy: Generic 3D models (rocks, ores)
- Loads GLB models via ModelCache
- Handles scale and rotation from manifest
- Supports depleted model swapping
-
FishingSpotVisualStrategy: Fishing spot particles
- Registers with ParticleManager for GPU-instanced water effects
- Handles ripple/splash/bubble animations
- Manages particle lifecycle on depletion
-
PlaceholderVisualStrategy: Fallback for missing models
- Uses PlaceholderInstancer for instanced colored cubes
- Color-coded by resource type (green=tree, brown=ore, blue=fishing)
- Automatically used when modelPath is null or “null” string
- Single Responsibility: Each strategy handles one rendering approach
- Open/Closed: Add new strategies without modifying ResourceEntity
- Testability: Strategies can be tested in isolation
- Maintainability: ~1700 lines of conditional logic replaced with clean delegation
- Manages InstancedMesh pools for placeholder resources (trees/ores with missing models)
- Color-coded: green (trees), brown (ores), blue (fishing spots)
- Max 1000 instances per resource type
- Initialized in
createClientWorld.tsalongside GLBTreeInstancer
- Fixed fishing spot particles persisting after depletion (guard re-registration when depleted, zero ripple phase offset on unregister)
- Fixed placeholder trees not rendering due to “null” string in modelPath (sanitize in ResourceSystem + createVisualStrategy factory)
Particle System
The shared package includes a centralized GPU-instanced particle system for high-performance visual effects. This system was introduced in PR #877 and provides a 97% reduction in draw calls with an 80% FPS improvement.ParticleManager Architecture
Unified particle routing system that dispatches particle events to specialized sub-managers:- ParticleManager: Central router with ownership tracking and event dispatching
- WaterParticleManager: GPU-instanced fishing spot effects (splash, bubble, shimmer, ripple)
- Reduces ~150 draw calls to 4 per fishing spot (97% reduction)
- FPS improvement: 65-70 → 120 on reference hardware (80% increase)
- ~450 lines of CPU animation code eliminated
- Zero CPU overhead for particle updates (all on GPU via TSL shaders)
- GPU-driven particle animations (parabolic arcs, wobble, twinkle, ring expansion)
WaterParticleManager Implementation
GPU-instanced water particle effects for fishing spots using InstancedMesh and TSL shaders: Technical Details:- 4 InstancedMeshes: splash, bubble, shimmer, ripple layers
- TSL NodeMaterials: GPU-computed animations using Three.js Shading Language
- InstancedBufferAttributes: Per-particle data storage
spotPos(vec3): fishing spot world centerageLifetime(vec2): current age (x), total lifetime (y)angleRadius(vec2): polar angle (x), radial distance (y)dynamics(vec4): peakHeight (x), size (y), speed (z), direction (w)
- Vertex Buffer Budget: 7 of 8 max attributes per particle layer
- position(1) + uv(1) + instanceMatrix(1) + spotPos(1) + ageLifetime(1) + angleRadius(1) + dynamics(1)
- Ripple Layer: 5 of 8 max attributes
- position(1) + uv(1) + instanceMatrix(1) + spotPos(1) + rippleParams(1)
- Pool Sizes: MAX_SPLASH=96, MAX_BUBBLE=72, MAX_SHIMMER=72, MAX_RIPPLE=24
- Splash: Parabolic arc trajectories with pop-in fade
arcY = peakHeight * 4 * t * (1-t)for parabolic motion- Snappy fade-in (12x rate), smooth fade-out (power 1.2)
- Lifetime: 0.6-1.2 seconds
- Bubble: Rising with lateral wobble
- Wobble frequency: direction * 4.0
- Drift pattern: sin(angle + t * wobbleFreq) * radius
- Lifetime: 1.2-2.5 seconds
- Shimmer: Surface sparkle with twinkle effect
- Fast twinkle using global time: sin(time8 + angle5) * sin(time13 + angle3)
- Circular wander pattern on water surface
- Lifetime: 1.5-3.0 seconds
- Ripple: Expanding ring with fade
- Phase-based scale: 0.15 + phase * 1.3
- Early fade (0-15%): linear ramp to 0.55 opacity
- Late fade (15-100%): power 1.5 decay from 0.55 to 0
- Continuous expansion driven by time uniform
- Net: Calm, gentle ripples (4 splash, 3 bubble, 3 shimmer, 2 ripples, burst every 5-10s)
- Bait: Medium activity (5 splash, 4 bubble, 4 shimmer, 2 ripples, burst every 3-7s)
- Fly: Active river fishing (8 splash, 5 bubble, 5 shimmer, 2 ripples, burst every 2-5s)
- Fish activity bursts fire 2-4 splash particles simultaneously
- Burst center randomized within 0.05-0.15 radius
- Burst particles have higher peak heights (0.25-0.6 vs 0.12-0.32)
- Burst particles cluster around burst center with 0.06 spread
- Burst timer resets to random interval after each burst
Usage Example
Integration with ResourceSystem
The ParticleManager is created and managed by ResourceSystem on the client:Lazy Registration Pattern
ResourceEntity uses a lazy registration pattern to handle timing issues where entities may be created before ResourceSystem starts:Extensibility Guide
To add new particle types (fire, magic, dust, blood):-
Create Sub-Manager Class: Create new file in
packages/shared/src/entities/managers/particleManager/ -
Instantiate in ParticleManager: Add to constructor
-
Add Routing Logic: Update register/unregister/move/handleEvent methods
-
Call update() and dispose(): Add to ParticleManager lifecycle methods
Rendering System
WebGPU-Only Architecture
As of February 2026, all rendering uses WebGPU exclusively:- Removed all WebGL detection and fallback code
- Removed
isWebGLAvailable(),isWebGLForced(),canTransferCanvas()functions RendererBackendtype is now only"webgpu"(removed"webgl")- All shaders use TSL (Three Shading Language) which only compiles to WebGPU
Key Exports
Entity Hierarchy
The entity system follows a clear inheritance pattern:Systems (Shared)
Located insrc/systems/shared/:
Collision System
The movement system includes a unified collision matrix for OSRS-accurate tile blocking:
Usage:
Data Manifests
Game content is defined in TypeScript files insrc/data/:
NPC data is loaded from JSON manifests at runtime by DataManager.
Add new NPCs in
world/assets/manifests/npcs.json.Entry Points
Server (index.ts)
Exports for server-side usage:Client (index.client.ts)
Exports for client-side usage:Dependencies
Procgen Dependency: The
@hyperscape/procgen package is listed as a devDependency (not a regular dependency) to break the circular dependency cycle. This allows TypeScript to find module declarations during type checking without creating a build cycle in Turbo. The import still resolves at runtime since both packages are always installed together in the workspace.Commits: f355276, 3b9c0f2, 05c2892 (Feb 25-26, 2026)Building
Shared must be built before other packages that depend on it. Turbo handles this automatically.
Key Patterns
ECS Architecture
All game logic uses Entity Component System. See ECS Concepts.Manifest-Driven Data
Game content defined insrc/data/. See Manifests.
Type Safety
Strong TypeScript typing throughout—noany types allowed. ESLint enforces this rule.
Dual Entry Points
index.ts: Server-side exports (includes Fastify, database utilities)index.client.ts: Client-side exports (browser-compatible)