This document outlines optional performance optimizations that can improve runtime responsiveness. These are improvements that can be applied if needed, but are not required for acceptable gameplay.
Bundle Size Context: A 20-25 MB bundle is acceptable for this feature-rich 3D web game engine. Bundle size optimization is deprioritized in favor of runtime performance and gameplay features.
Current Status:
- Startup Freeze: caused by compiling unique shaders for ~3000 individual mushroom instances (runtime optimization, medium priority).
- Runtime Lag: caused by heavy JavaScript loop iterating over thousands of Group objects (runtime optimization, medium priority).
- Load Time: slowed by sequential WASM calls for physics initialization (optional optimization, low priority).
Goals: stabilize frame rate without major refactoring. Apply if experiencing framerate issues.
- Raise
animatedFoliagesafety limit from 1000 to 3000. - Reduce procedural "extras" count if density is too high.
- Action: Ensure we target ~2000 initial objects, capping at 3000 max.
- The current culling relies on
obj.userData.radius. - Task: Audit
src/foliage/mushrooms.tsandflowers.tsto ensure they explicitly set accurateuserData.radius.- Example: A giant mushroom might need
radius: 8.0while a flower needsradius: 1.5.
- Example: A giant mushroom might need
- Task: Tighten render distance. Implemented dynamic value based on object size:
- Small flowers cull at 80m
- Regular mushrooms at 120m
- Giant mushrooms at 200m
- Clouds at 250m
- With 3000 objects, the shadow map is under heavy load.
- Task: Switch directional light shadow to
autoUpdate = falseand update it only when the sun moves significantly or foliage grows.
Goal: Eliminate the 2-minute freeze by reducing shader programs from ~3000 to ~12. Apply if startup freeze is problematic.
Status: ✅ COMPLETE - Using InstancedMesh with TSL materials
The mushroom-batcher.ts uses InstancedMesh which is even more efficient than 12 shared materials. Instead of creating materials per note, we use:
- Single
InstancedMeshfor all mushrooms - Instance attributes for color, scale, animation timing
- TSL shaders for all visual effects
Result: Shader compilation reduced from 3000 unique variants to ~8 materials total across all batchers.
Goal: Speed up world initialization by batching physics calls. Apply if world initialization takes too long.
- Task: Add exported function
addObstaclesBatch. - Signature:
void addObstaclesBatch(float* data, int count) - Data Layout: Flat array where each obstacle uses 9 floats:
[type, x, y, z, r, h, p1, p2, p3].
- Task: Create
uploadObstaclesBatch(objects)function for C++ physics. - Task: Create batch upload for AssemblyScript collision system.
- Logic: Single batch upload instead of 3000 individual calls.
- Replace sequential
foliageMushrooms.forEachwith singleuploadObstaclesBatchcall.
Goal: Support 10,000+ objects at 60 FPS. Apply if needing to scale to significantly more objects.
Status: ✅ COMPLETE
Moving THREE.Group objects to THREE.InstancedMesh has been implemented:
- ✅ MushroomBatcher: 4000 instances with TSL animations
- ✅ FlowerBatcher: Instanced flowers with color variation
- ✅ CloudBatcher: Instanced clouds
- ✅ FoliageBatcher: General foliage instancing
All heavy lifting is now GPU-side via TSL shaders.
Goal: Reduce initial load from 15MB to <500KB.
| Metric | Budget | Actual | Status |
|---|---|---|---|
| Main | 200 KB | 1.78 MB | ❌ 910% over |
| Total | 2 MB | 13.46 MB | ❌ 673% over |
- Asset Optimization: splash.png 1.5MB → splash.webp 155KB (90% reduction)
- Three.js Import: Use dynamic imports for optional features
- Tree Shaking: Remove 525 unused exports (820KB potential)
- Code Splitting: Implement lazy loading (693KB potential)
- core (Critical) - Scene, renderer, camera (~146KB)
- foliage (Lazy) - Trees, flowers, batchers (~293KB)
- audio (Interaction) - Audio system, music reactivity (~98KB)
- shaders (Lazy) - TSL materials (~195KB)
- wasm (Lazy) - Physics module (~98KB)
-
Implement Code Splitting in vite.config.ts
- Use dynamic imports for audio, foliage, weather systems
- Add preload hints to index.html
- Target: 50% reduction in initial load
-
Configure Server Compression
- Enable gzip/brotli on production server
- Target: 60-80% transfer size reduction
-
Tree Shaking Cleanup
- Audit and remove unused exports from common.ts
- Split large modules into smaller files
-
WASM Optimization
- Install wasm-opt (Binaryen):
apt install binaryen - Add
-O3flags to emscripten build - Target: 30-50% WASM size reduction
- Install wasm-opt (Binaryen):
-
Shader Caching
- Already implemented in shader-warmup.ts
- Verify all materials are warmed up at startup
| Metric | Current | Target | Priority |
|---|---|---|---|
| First Contentful Paint | ~3s | < 1.0s | High |
| Time to Interactive | ~8s | < 2.0s | High |
| Largest Contentful Paint | ~10s | < 2.5s | High |
| Total Bundle Size | ~15 MB | < 2 MB | High |
| Initial Load Size | ~15 MB | < 500 KB | High |
All optimization reports available in tools/build-optimizer/stats/:
OPTIMIZATION_REPORT.md- Comprehensive analysisbundle-analysis.html- Interactive bundle visualizationtree-shaking-report.md- Dead code analysiscode-splitting-plan.md- Chunking strategybudget-report.json- Performance budget check